本文共 6576 字,大约阅读时间需要 21 分钟。
Redux 是 JavaScript 状态容器,提供可预测化的状态管理。可以让你构建一致化的应用,运行于不同的环境(客户端、服务器、原生应用),并且易于测试。不仅于此,它还提供 超爽的开发体验,比如有一个时间旅行调试器可以编辑后实时预览
npm install redux --save
多数情况下,我们还需要使用 React 绑定库和开发者工具。
npm install redux-devtools --save-dev
整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于一个 唯一的store 中。
惟一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。
为了描述 action 如何改变 state tree ,你需要编写 reducers。
store.dispatch()
将 action 传到 store。type
属性是必须的,表示 Action 的名称。其他属性可以自由设置,参照 获取关于如何构造 action 的建议。redux约定 Action 内使用一个字符串类型的type
字段来表示将要执行的动作名称。
{ type: 'ADD_TODO'}
除了type
之外,Action还可以携带需要的数据。
{ type: 'ADD_ITEM', text: 'Learn Redux'}
Action Creator
View 要发送多少种消息,就会有多少种 Action。如果都手写,会很麻烦。可以定义一个函数来生成 Action,这个函数就叫 Action Creator。
const ADD_TODO = '添加 TODO'function addTodo(text) { return { type: ADD_TODO, text:text }}const action = addTodo('Learn Redux')
上面代码中,addTodo
函数就是一个 Action Creator,在 Redux 中的 Action Creator 只是简单的返回一个 action而已
在 实现中,当调用 Action Creator时,一般会触发一个 dispatch
function addTodoWithDispatch(text) { const action = { type: ADD_TODO, text:text } dispatch(action)}
在Redux 中只需把 Action Creator的结果传给 dispatch() 方法即可发起一次 dispatch 过程。
dispatch(addTodo(text))
或者创建一个被绑定的 Action Creator 来自动触发dispatch
const bindAddTodo = (text) => dispatch(addTodo(text))
然后直接调用这个函数即可完成一次dispatch过程
bindAddTodo(text)
注意:
store 里能直接通过store.dispatch()
调用dispatch()
方法,但是多数情况下我们选择使用 提供的 connect() 帮助器来调用。 可以自动把多个 action 创建函数 绑定到 dispatch() 方法上 Store 收到 Action 以后,必须给出一个新的 State,这样 View 才会发生变化。这种 State 的计算过程就叫做 Reducer。
Reducer 就是一个普通的函数
当被Redux调用的时候会给Reducer传递两个参数:State 和 Action
它会根据 Action 的type属性来对旧的 State 进行操作,返回新的State
const defaultState = 10const reducer = (state = defaultState, action) => { switch (action.type) { case Constants.INCREASE: return state + 1 case Constants.DECREASE: return state - 1 default: return state }}const state = reducer(10, { type: Constants.INCREASE})
上面代码中,reducer函数收到名为INCREASE
的 Action 后,就返回一个新的 State,作为加法的计算结果。
实际开发中,Reducer 函数不用像上面这样手动去调用,store.dispatch
方法会触发 Reducer 的自动调用,为此,Store 需要知道 Reducer 函数,做法就是在生成 Store 的时候,将 Reducer 传入到createStore
函数中。
import { createStore } from 'redux'import reducer from '../reducer'// 创建storeconst store = createStore(reducer)
上面代码中,createStore
接受 Reducer 作为参数,生成一个新的 Store。这样以后每当store.dispatch发送过来一个新的 Action,就会自动调用 Reducer,得到新的 State
Reducer 的拆分
真正开发项目的时候State会涉及很多功能,在一个Reducer函数中处理所有逻辑会非常混乱,所以需要拆分成多个子Reducer,每个子Reducer只处理它管理的那部分State数据。然后在由一个主rootReducers来专门管理这些子Reducer。
Redux提供了一个方法:专门来管理这些子Reducer
import {createStore, combineReducers} from 'redux'const list = (state = [], action) => { switch (action.type) { case ADD_ITEM: return [createItem(action.text), ...state] default: return state }}const counter = (state = defaultState, action) => { switch (action.type) { case Constants.INCREASE: return state + 1 case Constants.DECREASE: return state - 1 default: return state }}let rootReducers = combineReducers({list, counter})
combineReducers
生成了一个类似于Reducer的函数。为什么是类似于尼,因为它不是真正的Reducer,它只是一个调用Reducer的函数,只不过它接收的参数与真正的Reducer一模一样
combineReducers 核心源码解读
function combineReducers(reducers) { // 过滤reducers,把非function类型的过滤掉~ var finalReducers = pick(reducers, (val) => typeof val === 'function'); var defaultState = mapValues(finalReducers, () => undefined); return function combination(state = defaultState, action) { // finalReducers 是 reducers var finalState = mapValues(finalReducers, (reducer, key) => { // state[key] 是当前Reducer所对应的State,可以理解为当前的State var previousStateForKey = state[key]; var nextStateForKey = reducer(previousStateForKey, action); return nextStateForKey; }); // finalState 是 Reducer的key和stat的组合。。 }}
从上面的源码可以看出,combineReducers
生成一个类似于Reducer的函数combination。
注意:
当使用combination的时候,combination会把所有子Reducer都执行一遍,子Reducer通过action.type 匹配操作,因为是执行所有子Reducer,所以如果两个子Reducer匹配的action.type是一样的,那么都会匹配成功。import { createStore } from 'redux'import reducer from '../reducer'const store = createStore(reducer)
Store提供暴露出四个API方法
import { createStore } from 'redux'let { subscribe, dispatch, getState, replaceReducer} = createStore(reducer)
下面是createStore方法的一个简单实现,可以了解一下 Store 是怎么生成的。
const createStore = (reducer) => { let state; let listeners = []; const getState = () => state; const dispatch = (action) => { state = reducer(state, action); listeners.forEach(listener => listener()); }; const subscribe = (listener) => { listeners.push(listener); return () => { listeners = listeners.filter(l => l !== listener); } }; dispatch({}); return { getState, dispatch, subscribe };}
发起 Actions
现在我们已经创建好了 store ,让我们来验证一下!虽然还没有界面,我们已经可以测试数据处理逻辑了。
import { addTodo, toggleTodo, setVisibilityFilter, VisibilityFilters } from './actions'// 打印初始状态console.log(store.getState())// 每次 state 更新时,打印日志// 注意 subscribe() 返回一个函数用来注销监听器let unsubscribe = store.subscribe(() => console.log(store.getState()))// 发起一系列 actionstore.dispatch(addTodo('Learn about actions'))store.dispatch(addTodo('Learn about reducers'))store.dispatch(addTodo('Learn about store'))store.dispatch(toggleTodo(0))store.dispatch(toggleTodo(1))store.dispatch(setVisibilityFilter(VisibilityFilters.SHOW_COMPLETED))// 停止监听 state 更新unsubscribe()
可以看到 store 里的 state 是如何变化的:
img
可以看到,在还没有开发界面的时候,我们就可以定义程序的行为。而且这时候已经可以写 reducer 和 action 创建函数的测试。不需要模拟任何东西,因为它们都是纯函数。只需调用一下,对返回值做断言,写测试就是这么简单。
flow
store.dispatch(action)
let nextState = todoApp(previousState, action)
// 设置监听函数store.subscribe(listener)
function listerner() { let newState = store.getState(); component.setState(newState); }
阮一峰讲解redux
Redux中文文档
Redux其他系列教程
本系列教程是参照文章和进行的整合和拓展。更多更详细的Redux使用方式请参照上面的参考文献。
作者:光强_上海 链接:https://www.jianshu.com/p/faa98d8bd3fa 來源:简书 简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。