Redux源码解析

最近研究了Redux源码,作为React技术栈中的一份子,保持了其特有的小巧专一。代码总共不过百来行,其中包含了状态机,发布订阅模式以及高阶函数的应用,也确实值得我们学习借鉴。(以下源码略作修改和注释)

1、三大原则


Redux源码解析


单一数据源

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

State是只读的

唯一改变state的方法就是触发action, action是一个已发生事件的普通对象

使用纯函数来执行修改

为了描述action如何改变state tree,就需要reducer

源码目录


Redux源码解析


util/isPlainObject

<code>// 判断是不是纯对象,而非构造函数
export default function isPlainObject(obj) {
if(typeof obj != 'object' || obj === null) {
return false;
}
let proto = obj;
// 取到obj最终_proto_指向
while(Object.getPrototypeOf(proto)) {
proto = Object.getPrototypeOf(proto)
}
return Object.getPrototypeOf(obj) === proto;
}
复制代码/<code>

此纯对象的判断,判断对象最初的_proto_指向和_proto_最终指向的判断是否为同一个来判断


Redux源码解析


util/actionTypes.js

<code>const ActionTypes = {
INIT: '@@redux/INIT'
}
export default ActionTypes;
复制代码/<code>

createStore.js

作为redux中的C位,其中的方法包括getState, dispatch, subscribe方法

<code>export default function createStore(reducer, preloadedState) {
if(typeof reducer != 'function') {
throw new Error('reducer不是函数')
}
let currentReducer = reducer; // 状态处理器
let currentState = preloadedState; // 初始状态
let currentListeners = []; // 监听函数队列
function getState() {
return currentState;
}

function dispatch(action) {
if(!isPlainObject(action)) {
throw new Error('action不是纯对象')
}
if(typeof action.type == 'undefined') {
throw new Error('action的type未定义')
}
// 获取当前state
currentState = createReducer(currentState, action);
// 发布订阅模式
for(let i=0; i<currentlisteners.length> const listener = currentListeners[i];
listener();
}
return action;
}

// 订阅

function subscribe(listener) {
let subscribed = true;
currentListeners.push(listener);
// 取消订阅
return function unsubscribe() {
// 重复订阅优化
if(!subscribed) {
return ;
}
const index = currentListeners.indexOf(listener);
currentListeners.splice(index, 1);
subscirbed = false;
}
}

// 创建store初始化值
dispatch({type:ActionTypes.INIT});

return {
getState,// 返回状态
dispatch,// 派发动作
subscirbe
}
}
复制代码/<currentlisteners.length>/<code>

combineReducers.js

将多个reducer合并到一个对象中,方便维护

<code>export default function(reducers) {
// 返回reducers名称的数组
const reducerKeys = Object.keys(reducers);
return function (state = {}, action) {
const nextState = {};
for(let i = 0; i < reducerKeys.length; i++) {
const key = reducerKeys[i];
const reducer = reducers[key];
const previousStateForKey = state[key]; // 老状态
const nextStateForKey = reducer(previousStateForKey, action); // 新状态
nextState[key] = nextStateForKey;
}
return nextState;
}
}

复制代码/<code>

bindActionCreators.js

将dispatch整合到action中

<code>function bindActionCreator(actionCreator, dispatch) {
return function() {
return dispatch(actionCreator.apply(this, arguments))
}
}
/**
*
* @param {Function|Object} actionCreators
* @param {Function} dispatch 对redux Store生效的dispatch方法
* @returns {Function|Object} 如果返回对象的话类似与原始对象,但是每个对象元素添加了dispatch方法
*/
export default function bindActionCreators(actionCreators, dispatch) {
if(typeof actionCreators === 'function') {
return bindActionCreator(actionCreators, dispatch)
}
if(typeof actionCreators !== 'object' || actionCreators === null) {
throw new Error(
`bindActionCreators应该是一个对象或者函数类型, 而不是接收到${
actionCreators === null ? 'null' : typeof actionCreators
}. ` +
`是不是这样引用 "import ActionCreators from" 而不是 "import * as ActionCreators from"?`
)
}

// actionCreators为对象的时候
const boundActionCreators = {}
for (const key in actionCreators) {
const actionCreator = actionCreators[key]
if (typeof actionCreator === 'function') {
boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
}
}
return boundActionCreators
}
复制代码/<code>

compose.js

这一块灵活应用了reduce方法和高阶函数,实现参数函数从右向左执行

<code>/**
*
* @param {...any} funcs
* @returns 包含从右向左执行的函数参数。比如,compose(f, g, h) 等同于 (...args) => f(g(h(...args)))
*/
export default function compose(...funcs) {
if(funcs.length === 0) {
return arg => arg
}
if(funcs.length === 1) {
return funcs[0]
}

return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
复制代码/<code>

applyMiddleware.js

<code>import compose from './compose'

export default function applyMiddleware(...middlewares) {
return createStore => (...args) => {
const store = createStore(...args)
let dispatch = () => {
throw new Error('构造中间键的时候不能运行dispatch,否则其他中间键将接收不到dispatch')
}
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
}

const chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)

return {
...store,
dispatch
}

}
}
复制代码/<code>

index.js

<code>import createStore from './createStore';
import combineReducers from './combineReducers';
import bindActionCreators from './bindActionCreators';
import applyMiddleware from './applyMiddleware';
export {
createStore,//创建仓库
combineReducers,//合并reducers
bindActionCreators,//把actionCreator 和 dispatch方法绑定在一起
applyMiddleware
}
复制代码/<code>

中间键实践

redux-thunk中间键实现

<code>function createThunkMiddleware(extraArgument) {
return ({dispatch, getState}) => next => action => {
if(typeof action === 'function') {
return action(dispatch, getState, extraArgument)
} else {
next(action)
}
}
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
复制代码/<code>

redux-promise

<code>// Promise判断:有then方法的函数对象
function isPromise(obj) {
return !obj&&(typeof obj == 'object' || typeof obj == 'function') && (typeof obj.then == 'function')
}

export default function({dispatch, getState}) {
return next => action => {
return isPromise(action.payload)? action.payload.then(result => {
dispatch({...action, payload: result})

}).catch((error) => {
dispatch({...action, payload:error, error: true});
return Promise.reject(error)
}) : next(action)
}
}
复制代码/<code>

React-Redux实现

connect.js

<code>import React, {Component} from 'react';
import { bindActionCreators } from '../redux';
import ReduxContext from './context';

export default function(mapStateToProps, mapDispatchToProps) {
return function(WrappedComponent) {
return class extends Component {
static contextType = ReduxContext;
constructor(props, context) {
super(props);
this.state = mapStateToProps(context.store.getState());
}
componentDidMount() {
this.unsubscribe = this.context.store.subscribe(() => {
this.setState(mapStateToProps(this.context.store.getState()))
})
}
componentWillMount() {
this.unsubscribe()
}
render() {
let actions = {}
if(typeof mapDispatchToProps == 'function') {
actions = mapDispatchToProps(this.context.store.dispatch)
} else {
actions = bindActionCreators(mapDispatchToProps, this.context.store.dispatch);
}

return <wrappedcomponent>
}
}
}
}
复制代码/<code>

Provider.js

<code>import React, { Component } from 'react';
import ReduxContext from './context';
export default class Provider extends Component {
render() {
return (
<reduxcontext.provider>
{this.props.children}
/<reduxcontext.provider>
)
}
}
复制代码/<code>

context.js

<code>import React from 'react';
const ReduxContext = React.createContext(null);
export default ReduxContext;
复制代码/<code>

index.js

<code>import Provider from './Provider';
import connect from './connect';
export {
Provider,
connect
}/<code>


作者:金金金呀
链接:https://juejin.im/post/5e9042ad6fb9a03c3f1ea7c0
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


分享到:


相關文章: