【Redux】Redux核心概念,API锦囊以及源码
核心概念
Store
Store就是一个仓库,它存储了所有的状态(State)。
State
state 就是一个普通的对象(plain object)。
{
todos: [{
text: 'Eat food',
completed: true
}, {
text: 'Exercise',
completed: false
}],
visibilityFilter: 'SHOW_COMPLETED'
}
Action
一个Action就是一个动作,这个动作的目的是更改Store中的某个状态。Action 就是一个普通的Javascript 对象。
{ type: 'ADD_TODO', text: 'Go to swimming pool' }
{ type: 'TOGGLE_TODO', index: 1 }
{ type: 'SET_VISIBILITY_FILTER', filter: 'SHOW_ALL' }
但是如果要改变一个state, 我们必须要dispatch 一个action。
Reducer
reducer 就是把state跟action联系在一起。reducer 是一个纯函数,它以state与action做为参数,同时返回新的state。
function visibilityFilter(state = 'SHOW_ALL', action) {
if (action.type === 'SET_VISIBILITY_FILTER') {
return action.filter
} else {
return state
}
}
Redux本身就是一个单纯的状态机,Store存放了所有的状态,Action是一个改变状态的通知,Reducer接收到通知就更改Store中对应的状态。
API锦囊
createStore(reducer, [preloadedState], [enhancer])
import { createStore } from 'redux'
function todos(state = [], action) {
switch (action.type) {
case 'ADD_TODO':
return state.concat([action.text])
default:
return state
}
}
const store = createStore(todos, ['Use Redux'])
store.dispatch({
type: 'ADD_TODO',
text: 'Read the docs'
})
console.log(store.getState())
// [ 'Use Redux', 'Read the docs' ]
createStore支持了第二个参数,这个参数官方称为enhancer,顾名思义他是一个增强器,用来增强store的能力的。
store
getState()
store.getState:一个简单的方法,返回当前的state。
dispatch(action)
发出action的方法,每次dispatch action都会执行reducer生成新的state,然后执行subscribe注册的回调。
import { createStore } from 'redux'
const store = createStore(todos, ['Use Redux'])
function addTodo(text) {
return {
type: 'ADD_TODO',
text
}
}
store.dispatch(addTodo('Read the docs'))
store.dispatch(addTodo('Read about the middleware'))
subscribe(listener)
订阅state的变化,当state变化的时候执行回调,可以有多个subscribe,里面的回调会依次执行。
function select(state) {
return state.some.deep.property
}
let currentValue
function handleChange() {
let previousValue = currentValue
currentValue = select(store.getState())
if (previousValue !== currentValue) {
console.log(
'Some deep nested property changed from',
previousValue,
'to',
currentValue
)
}
}
const unsubscribe = store.subscribe(handleChange)
unsubscribe()
replaceReducer(nextReducer)
combineReducers(reducers)
Redux提供了combineReducers,可以让我们为不同的模块写自己的reducer,最终将他们组合起来。
// reducers/todos.js
export default function todos(state = [], action) {
switch (action.type) {
case 'ADD_TODO':
return state.concat([action.text])
default:
return state
}
}
// reducers/counter.js
export default function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
}
// reducers/index.js
import { combineReducers } from 'redux'
import todos from './todos'
import counter from './counter'
export default combineReducers({
todos,
counter
})
// App.js
import { createStore } from 'redux'
import reducer from './reducers/index'
const store = createStore(reducer)
console.log(store.getState())
// {
// counter: 0,
// todos: []
// }
store.dispatch({
type: 'ADD_TODO',
text: 'Use Redux'
})
console.log(store.getState())
// {
// counter: 0,
// todos: [ 'Use Redux' ]
// }
applyMiddleWare(...midlleware)
中间件就是加强dispatch的功能,用新的dispatch替换老的dispatch,这就是装饰者模式。其实前面enhancer也是一个装饰者模式,传入一个createStore,在createStore执行前后加上些代码,最后又返回一个增强版的createStore。
import { createStore, applyMiddleware } from 'redux'
import todos from './reducers'
function logger({ getState }) {
return next => action => {
console.log('will dispatch', action)
// Call the next dispatch method in the middleware chain.
const returnValue = next(action)
console.log('state after dispatch', getState())
// This will likely be the action itself, unless
// a middleware further in chain changed it.
return returnValue
}
}
const store = createStore(todos, ['Use Redux'], applyMiddleware(logger))
store.dispatch({
type: 'ADD_TODO',
text: 'Understand the middleware'
})
// (These lines will be logged by the middleware:)
// will dispatch: { type: 'ADD_TODO', text: 'Understand the middleware' }
// state after dispatch: [ 'Use Redux', 'Understand the middleware' ]
bindActionCreators(actionCreators, dispatch)
compose(...functions)
import { createStore, applyMiddleware, compose } from 'redux'
import thunk from 'redux-thunk'
import DevTools from './containers/DevTools'
import reducer from '../reducers'
const store = createStore(
reducer,
compose(applyMiddleware(thunk), DevTools.instrument())
)
demo以及源码解析
例子:git clone https://github.com/lxchuan12/redux-analysis.git 中的examples 的例子
git clone http://github.com/lxchuan12/redux-analysis.git
cd redux-analysi/redux
npm i
npm run build
启动examples的例子启动不起来,可以用python启动。python -m SimpleHTTPServer
通过调试计数器例子的学习 redux 源码
查看examples/index.1.redux.getState.dispatch.html文件,用debugger来调试。
会看到Redux 包含的几个方法:
- applyMiddleware: ƒ applyMiddleware() 函数是一个增强器,组合多个中间件,最终增强store.dispatch函数,dispatch时,可以串联执行所有中间件。
- bindActionCreators: ƒ bindActionCreators(actionCreators, dispatch) 生成actions,主要用于其他库,比如react-redux。
- combineReducers: ƒ combineReducers(reducers) 组合多个reducers,返回一个总的reducer函数。
- compose: ƒ compose() 组合多个函数,从右到左,比如:compose(f, g, h) 最终得到这个结果 (...args) => f(g(h(...args))).createStore: ƒ createStore(reducer, preloadedState, enhancer) 生成 store 对象
Store包含的几个方法:
- dispatch: ƒ dispatch(action) 派发动作,也就是把subscribe收集的函数,依次遍历执行
- subscribe: ƒ subscribe(listener) 订阅收集函数存在数组中,等待触发dispatch依次执行。返回一个取消订阅的函数,可以取消订阅监听。
- getState: ƒ getState() 获取存在createStore函数内部闭包的对象。
- replaceReducer: ƒ replaceReducer(nextReducer) 主要用于redux开发者工具,对比当前和上一次操作的异同。有点类似时间穿梭功能。
- Symbol(observable): ƒ observable()
Redux 中间件相关源码
查看examples/js/middlewares.logger.example.js ,通过debuger来查看。
其中中间件的写法:
// examples/js/middlewares.logger.example.js
function logger1({ getState }) {
return next => action => {
console.log('will dispatch--1--next, action:', next, action)
// Call the next dispatch method in the middleware chain.
const returnValue = next(action)
console.log('state after dispatch--1', getState())
// This will likely be the action itself, unless
// a middleware further in chain changed it.
return returnValue
}
}
// 省略 logger2、logger3
把接收的中间件函数logger1, logger2, logger3放入到 了middlewares数组中。
Redux.applyMiddleware最后返回两层函数。 把中间件函数都混入了参数getState和dispatch。
var store = Redux.createStore(counter, Redux.applyMiddleware(logger1, logger2, logger3))
最后这句其实是返回一个增强了dispatch的store对象。
而增强的dispatch函数,则是用Redux.compose(...functions)进行串联起来执行的。
Redux中间件理解
Redux中间件的用法
createStore函数接收参数为(reducer, [initState], enhancer)
import {createStore, applyMiddleware} from 'redux';
import thunkMiddleware from 'redux-thunk';
import rootReducer from '../reducers';
import defaultState from './defaultState';
export default function configureStore() {
let store = createStore(
rootReducer,
defaultState,
applyMiddleware(
thunkMiddleware
)
);
return store;
}
Redux中间件的写法
例如:redux-thunk,可以处理异步操作
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => next => action => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;
redux-thunk 中间件的功能很简单。首先检查参数 action 的类型,如果是函数的话,就执行这个 action 函数,并把 dispatch, getState, extraArgument 作为参数传递进去,否则就调用 next 让下一个中间件继续处理 action 。另外还有一些中间件如redux-logger(打印日志)、redux-promise(处理异步操作,但是返回Promise对象)、redux-saga等。
我们总结Redux 中间写法的格式:
// 外层
function createThunkMiddleware (extraArgument){
// 第一层
return function ({dispatch, getState}){
// 第二层
return function (next){
// 第三层
return function (action){
if (typeof action === 'function'){
return action(dispatch, getState, extraArgument);
}
return next(action);
};
}
}
}
Redux中间件返回的是什么
我们从applymiddleware 的源码来看。
export default function applyMiddleware(...middlewares) {
return createStore => (...args) => {
const store = createStore(...args)
let dispatch = () => {
throw new Error(
)
}
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
}
const chain = middlewares.map(middleware => middleware(middlewareAPI))
// 将不同的 middlewares 一层一层包裹到原生的 dispatch 之上
// compose 将 chain 中的所有函数组装成一个新的函数,即新的 dispatch
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
applyMiddleware的执行结果最终是返回store的所有方法和一个dispatch方法。
const chain = middlewares.map(middleware => middleware(middlewareAPI));
dispatch = compose(...chain)(store.dispatch)
上面的这俩行代码是所有中间件被串联起来的核心部分实现,它们也决定了中间件内部为啥会有我们上面提到的三层柯里化的固定格式。
参考:
https://juejin.cn/post/6844904191228411911
https://www.jianshu.com/p/a4ab789790b2
https://www.cnblogs.com/rock-roll/p/10763383.html