关于 Redux,你想知道的

什么是 Redux

在现代前端项目中,有两大方面最令开发者头疼:数据变化异步流
而 Redux 通过明确的指令让数据变化可预测

如何使用

Basic Usage

	import { createStore } from 'redux'

	function reducer(prevState = { count = 0 }, action) {
		switch (action.type) {
			case 'INCREASE':
				return prevState + 1
			case 'DECREASE':
				return prevState - 1
			default:
				return prevState
		}
	}
	
	const store = createStore(reducer)

	store.getState()
	store.dispatch({ type: 'INCREASE' })
	store.subcribe(() => console.info('curr state: ', store.getState()))
	

简单来说就是使用明确的 action 来更新 state,从而让数据变化行为可预测和易于复现。

基本概念

action

在 Redux 中,state 是只读的,唯一改变 state 的方法就是触发 action;
action 是一个描述已发生事件的普通对象:

	store.dispatch({ type: 'COMPELETE', index: 1 })

	store.dispatch({ type: 'CHANGE_VIEW', view: 'ALL' })

另外还有 action 创建函数,顾名思义,即创建 action 的函数:

	function addTodo(text) {
		return {
			type: 'ADD',
			text,
		}
	}

	store.dispatch(addTodo('Text'))

reducer

为了描述 action 如何改变 state tree,需要编写 reducer;
reducer 是一个纯函数,接收 previous state 和 action 并返回 new state:

	function reducer(prevState = { count: 0 }, action) {
		switch (action.type) {
			case 'INCREASE':
				return prevState + 1
			case 'DECREASE':
				return prevState - 1
			default:
				return prevState
		}
	}

随着应用扩大,可以编写小的 reducer,分别更新 state tree 的一部分:

	function changeView(state = 'ALL', action) {
		if (action.type) {
			return action.type
		} else {
			return state
		}
	}

	function updateTodo(state = [], action) {
		switch (action.type) {
			case 'ADD':
				return state.concat(action.item)
			case 'DEL':
				return state.filter(item => item.index !== action.index)
			default:
				return state
		}
	}

	function todoApp(state, action) {
		return {
			view: changeView(state.view, action),
			todo: updateTodo(state.todo, action),
		}
	}

Tip:
1. 不要修改 state,可以使用扩展运算符覆盖之前的变量:
return { ...state, ...newState }
或使用对象克隆返回新对象:
Object.assign({}, state, newState)
2. 使用 default 分支可以在未知 action 下返回旧的 state
3. 使用 combineReducer()合并 reducer:

	import { combineReducers } from 'redux'

	const todoApp = combineReducers({
		view,
		todo,
	})

	// 这与上面的写法等价:
	function todoApp(state, action) {
		return {
			view: changeView(state, action),
			todo: updateTodo(state, action),
		}
	}
	

combineReducers() 所做的只是生成一个函数,这个函数调用你的一系列 reducer;
每个 reducer 根据它们的 key 来筛选出 state 中的一部分并进行处理;
最后再将所有 reducer 的结果合并成一个 state tree。

store

整个应用的 state 被存储在一颗 object tree 中,并且这个 object tree 只存在于唯一一个 store 中:

	store.getState()
	
	{
		project: {
			_id: '111',
			name: 'Project Name',
			type: 'normal'
		},
		tasks: [
			{
				_id: '222',
				_projectId: '111'
				content: 'Task Content 1',
			},
			{
				_id: '333',
				_projectId: '111'
				content: 'Task Content 2',
			},
		]
	}

在上面我们介绍了根据 action 来描述发生了什么,和 reducer 来根据 action 更新 state;
store 就是把它们联系到一起的对象,store 有以下职责:

  • 维持应用的 state
  • 提供 getState() 获取 state
  • 提供 dispatch(action) 方法更新 state
  • 通过 subcribe(listener) 注册监听器;
  • 通过 subscribe(listener) 返回的函数注销监听器。

使用方法:
createStore(reducer, initialState)

数据流

严格的单向数据流 是 Redux 架构的设计核心
这意味着应用中所有数据都遵循着相同的生命周期:

  1. 调用 store.dispatch(action)
  2. Redux store 调用传入的 reducer 函数;
    store 会将两个参数参入 reducer:当前的 state tree 和 action;
    由 reducer 返回更新的 state
  3. 根 reducer 应该把多个子 reducer 合并成一个单一的 state tree;
    可使用 Redux 原生提供的 combineReducers() 辅助函数
  4. Redux store 保存了根 reducer 返回的完整 state tree;
    这个树就是应用的下一个 state,所有订阅 store.subcribe(listener) 的监听器都将被调用,可以使用 store.getState() 获取最新的 state

进阶指南

异步 Action

在实际项目中,数据很可能是从服务端发起请求异步获取的,
如何将同步 action 创建函数与网络请求结合呢?
标准做法是使用 redux-thunk 中间件

通过使用指定的中间件,action 创建函数除了返回 action 对象外还可以返回函数
这时,这个 action 创建函数就成了 thunk

当 action 创建函数返回函数时,这个函数会被 redux-thunk 中间件执行
这个函数不需要保持纯净,它可以带有副作用如执行异步请求 API
同时,这个函数还可以 dispatch action

	import fetch from 'fetch'
	import thunkMiddleware from 'redux-thunk'
	import { createLogger } from 'redux-logger'
	import { createStore, applyMiddleware } from 'redux'
	
	import rootReducer from './action.js'

	const loggerMiddleware = createLogger()

	const store = createStore(
		rootReducer,
		applyMiddleware(
			thunkMiddleware,
			loggerMiddleware,
		)
	)
	
	function receivePosts(page, json) {
		return {
			type: 'RECEIVE_POST',
			page,
			posts: json,
		}
	}
	
	function fetchPosts(page) {
		// redux-thunk 中间件知道如何处理函数
		// 这里将 dispatch 方法通过参数形式传递给函数
		// 以此来让它自己也能 dispatch action
		return dispatch => {
			// 返回一个等待处理的 promise
			return fetch(`https://www.baidu.com?page=${page}`)
					.then(
						response => response.json(),
						error => console.info(`An error occurred: ${error}`)
					)
					.then(json => dispatch.action(receivePosts(page, json)))
		}
	}

	store.dispatch(fetchPosts('redux'))
		 .then(() => console.info(store.getState()))
		 

redux-thunkredux-promise 这样支持异步的中间件都封装了 store 的 dispatch( ) 方法,以此来让你 dispatch 一些除了 action 以外的内容,如 函数 或 Promise;

你所使用的任何 middleware 都可以以自己的方式解析你 dispatch 的任何内容,并继续传递 action 给下一个 middleware

当 middleware 链 中最后一个 middleware 开始 dispatch action 时,必须使用普通的 action 对象 将处理流程带回同步方式

中间件

对于中间件,如果接触过 Express 或 Koa 等 服务端 框架的同学应该不陌生;
在这类框架中,中间件是指可以被 嵌入 在框架接收到请求 到 返回响应 之间的代码;
同时,中间件可被链式调用,因此可以在项目中使用多个独立的第三方中间件;

在 Redux 中,中间件的概念是类似的:
它提供的是位于 action 发起后,到达 reducer 之前的扩展点

	const reduxMiddleware = store => next => action => {
		console.info('dispatching: ', action)
		const result = next(action)
		console.info('next state': store.getState())
		return result
	}

	function applyMiddleware(store, middlewares) {
		middlewares = middlewares.slice()
		middlewares.reverse()

		let dispatch = store.dispatch
		middlewares.forEach(middleware =>
			dispatch = middleware(store)(dispatch)
		)
	}

	return Object.assign({}, store, { dispatch })
	

同类对比

MobX

扩展

Immutable.js
React Redux

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值