源码 状态机_Redux源码解析

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

1、三大原则

b7a23325997615e6f499f9a38ca34063.png

单一数据源

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

State是只读的

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

使用纯函数来执行修改

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

源码目录

e45985cd2b0a3b384cb0f5cfb27ee087.png

util/isPlainObject

// 判断是不是纯对象,而非构造函数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;}复制代码

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

07d9b1b9c56624b45707aa78b5a75a1f.png

util/actionTypes.js

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

createStore.js

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

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

combineReducers.js

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

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;    }}复制代码

bindActionCreators.js

将dispatch整合到action中

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}复制代码

compose.js

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

/** *  * @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)))}复制代码

applyMiddleware.js

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        }    }}复制代码

index.js

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

中间键实践

redux-thunk中间键实现

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;复制代码

redux-promise

// 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)    }}复制代码

React-Redux实现

connect.js

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             }        }    }}复制代码

Provider.js

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

context.js

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

index.js

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


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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值