本文主要参考该博文:8k字 | Redux/react-redux/redux中间件设计实现剖析
为了便于本人阅读,概况总结了上述文章,并基于最新 React
修改了部分接口的实现;有兴趣的同学,可以直接阅读上文
为什么要有 redux
管理公共状态,便于组件间的通信
redux 的设计与实现
设计
要实现状态的管理,主要要有以下几步
- 状态变量
currentState
- 状态的存、取,即
setter
、getter
方法 - 当状态发生改变的时候,还要对数据进行广播,就需要有
subscribe
方法
所以 store
的大致形状如下
const createStore = () => {
let currentState = {}; // 公共状态
function getState() {} // getter
function dispatch() {} // setter
function subscribe() {} // 发布订阅
return { getState, dispatch, subscribe };
}
getter 方法的实现
返回当前状态
const createStore = () => {
// ...
function getState () {
return currentState;
}
}
dispatch 方法的实现
传入一个 action
对象,有条件、具名的修改 store
的数据
// 仿照 reducer 的写法
const initialState = {
count: 0
};
function Reducer(state = initialState, action) {
switch(action.type) {
case 'plus':
return {
...state,
count: state.count + 1
}
case 'substract':
return {
...state,
count: state.count - 1
}
default:
return initialState;
}
}
const createStore = () => {
// ...
function dispatch(action) {
currentState = reducer(currentState, action)
}
}
subscribe 方法的实现
参考 观察者-订阅者 模式,如下
// 观察者
class Observer {
constructor(fn) {
this.update = fn;
}
}
// 被观察者
class Subject {
constructor() {
this.observers = [];
}
addObserver(observer) {
this.observers.push(observer);
}
notify() {
this.observers.forEach(observer => {
observer.update();
})
}
}
var subject = new Subject();
var observer1 = new Observer(() => { console.log(1); });
var observer2 = new Observer(() => { console.log(2); });
subject.addObserver(observer1);
subject.addObserver(observer2);
subject.notify();
// 1
// 2
基于上述模式,我们就可以实现当数据更新的时候,对数据进行广播更新
const createStore = () => {
// ...
let observers = []; // 观察者队列,用于广播更新
function dispatch(action) {
currentState = reducer(currentState, action);
observers.forEach(fn => fn()); // 依次执行观察者队列中的方法
}
function subscribe(fn) {
observers.push(fn);
}
}
最终代码
// 仿照 reducer 的写法
const initialState = {
count: 0
};
function reducer(state = initialState, action) {
switch (action.type) {
case 'plus':
return {
...state,
count: state.count + 1
}
case 'substract':
return {
...state,
count: state.count - 1
}
default:
return initialState
}
}
const createStore = (reducer) => {
// 公共状态
let currentState = {};
// 观察者
let observers = [];
// getter
function getState() {
return currentState;
}
// setter
function dispatch(action) {
currentState = reducer(currentState, action);
observers.forEach(fn => fn()); // 当数据发生变化时,触发对应的方法
}
// 发布订阅
function subscribe(fn) {
observers.push(fn);
}
// 初始化参数
dispatch({type: ''})
return { getState, dispatch, subscribe };
}
// 使用
const store = createStore(reducer);
store.subscribe(() => { console.log(1); });
store.subscribe(() => { console.log(2); });
store.dispatch({type: 'plus'})
// 1
// 2
react-redux 的设计与实现
提供了 Provider
、connect
这两个 API
Provider
:将store
借助React Context
进行数据传递connect
:将getState
、dispatch
方法合并进了props
,传递给组件,并实现了自动订阅更新
Provider 方法的实现
const ReduxContext = React.createContext(null);
class Provider extends React.Component {
render() {
return (
<ReduxContext.Provider value={this.props.store}>
{this.props.children}
</ReduxContext.Provider>
)
}
}
connect 方法的实现
connect(mapStateToProps, mapDispatchToProps)(App) // 高阶组件
connect
接收两个参数
- mapStateToProps
- 将
store
中的变量转换为UI
组件中的同名变量
- 将
- mapDispatchToProps
- 建立
UI
组件与store.dispatch
方法的映射
- 建立
通过 React
的高阶组件实现
function connect(mapStateToProps, mapDispatchToProps) {
return function (Component) {
class Connect extends React.Component {
componentDidMount() {
this.context.subscribe(this.handleStoreChange.bind(this));
}
handleStoreChange() {
// 触发组件更新,也可以通过 setState 等方式触发组件更新
this.forceUpdate();
}
render() {
return (
<Component
{...this.props}
{...mapStateToProps(this.context.getState())} // 转变 stroe 中的同名变量
{...mapDispatchToProps(this.context.dispatch)} // 建立与 store.dispatch 方法的映射
/>
)
}
}
Connect.contextType = ReduxContext;
return Connect;
}
}
简易使用
沿用 redux 的例子,使用上述接口
function mapStateToProps(state) {
return {
count: state.count
};
}
// 定义对应的 action
const addCountAction = {
type: 'plus'
}
function mapDispatchToProps(dispatch) {
return {
addCount: () => {
dispatch(addCountAction);
}
}
}
class Test extends React.Component {
render() {
// console.log(this.props);
return (
<div>
{this.props.count}
<button onClick={() => this.props.addCount()}>Add</button>
</div>
)
}
}
const App = connect(mapStateToProps, mapDispatchToProps)(Test);
ReactDOM.render(
<Provider store={createStore(reducer)}>
<App />
</Provider>,
document.getElementById('root')
);
redux Middleware 实现
使用中间件拦截 dispatch
到 reducer
的这个过程,从而达到增强 dispatch
功能
实现思路
改装 dispatch
,从而强化 createStore
功能
改装 createStore
当 heightener
存在的时候,则直接通过 heightener
接收强化 createStore
const createStore = (reducer, heightener) => {
if(heightener) {
heightener(createStore)(reducer);
}
// ...
}
定义 applyMiddleware 中间件函数
即 heightener
入参函数
const applyMiddleware = (...middlewares) => createStore => reducer => {
const store = createStore(reducer);
let { getState, dispatch } = store; // 获取 store 中的 getState,dispatch 方法
const params = {
getState,
dispatch: (action) => dispatch(action)
};
const middlewareArr = middlewares.map(middleware => middleware(params));
dispatch = compose(...middlewareArr)(dispatch);
return { ...store, dispatch };
}
function compose(...fns) {
if (fns.length === 0) return arg => arg;
if (fns.length === 1) return fns[0];
return fns.reduce((res, cur) => (...args) => res(cur(...args)));
}
简单使用
const logger = store => next => action => {
console.log('log1');
let result = next(action);
return result;
}
const thunk = store => next => action => {
console.log('thunk');
return typeof action === 'function' ? action(store.dispatch) : next(action);
}
const logger2 = store => next => action => {
console.log('log2');
let result = next(action);
return result;
}
let store = createStore(reducer, applyMiddleware(logger, thunk, logger2))
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);