redux的原理及简单实现

本文深入探讨了Redux的基本原理,包括如何创建store、实现异步action以及使用thunk中间件。同时,解释了`combineReducers`在大型项目中的作用,帮助将多个reducer组合在一起。通过实例代码,详细解析了thunk的工作流程,展示了如何在应用中调用异步action。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、redux

抛开react,如果只是仅仅使用redux,就像如下示例:

const initialState = {
    name: 'Jack',
    age: 27,
    gender: 'boy'
};

function reducer(state = initialState, action) {
    switch(action.type) {
        case 'add':
            return { ...state, age: state.age + 1 };
        default:
            return state;
    }
}
const store = createStore(reducer);
store.subscribe(function() {
    console.log(store.getState());
});
store.dispatch({ type: 'add' });

这里其实就是一个createStore函数,返回了dispatch、subscribe、getState这三个方法,如下就是redux的简单实现:

function createStore(reducer, enhancer) {
    // createStore(reducer, applyMiddleware(thunk));
    if (enhancer) {
        return enhancer(createStore)(reducer);
    }
    let state;
    let listeners = [];
    function subscribe(fn) {
        fn && listeners.push(fn);
    }
    function dispatch(action) {
        state = reducer(state, action);
        listeners.forEach(lis => lis());
        return action;
    }
    function getState() {
        return state;
    }
    // 用于初始化state,其中type任意名称
    dispatch({ type: '@Init_XXX_dXXxx' });
    return {
        subscribe,
        dispatch,
        getState
    };
}

2、redux实现异步action

正常要想实现异步action,需要引入thunk,如:

import thunk from 'redux-thunk';
import { createStore, applyMiddleware } from 'redux';
import reducers from './reducer';

const store = createStore(reducers, applyMiddleware(thunk));

其中通过applyMiddleware来合并各种thunk。先让我们来看看一个thunk的基本结构:

const thunk = ({ getState, dispatch }) => next => action => {
    if (typeof action === 'function') {
        return action(dispatch, getState);
    }
    return next(action);
};
export default thunk;

这是一个三层嵌套函数,通过next串联各个thunk。

让我们再来看看applyMiddleware的实现:

function applyMiddleware(...middlewares) {
    return (createStore) => (...args) => {
        const store = createStore(...args);
        let dispatch = store.dispatch;
        const midApi = {
            getState: store.getState,
            // 这里保证dispatch是下面compose后,最终生成的dispatch
            dispatch: (...args) => dispatch(...args)
        };
        let midwaresChain = middlewares.map(mw => mw(midApi));
        dispatch = compose(...midwaresChain)(store.dispatch);
        return {
            ...store,
            dispatch
        };
    };
}

// fn1(fn2(fn3(...args)))
function compose(...middlewares) {
    if (!middlewares) {
        return arg => arg
    }
    return middlewares.reduce((ret, func) => {
        return (...args) => ret(func(...args))
    });
}

首先对每个thunk传入midApi进行调用,这里midApi对应thunk的第一个参数({ getState, dispatch }),最后形成的数组里的每项都各自对getState和dispatch形成一个闭包。注意:这里的dispatch就是最终compose完成后的dispatch

let midwaresChain = middlewares.map(mw => mw(midApi));

然后对midwaresChain调用compose函数,最后返回的函数为类似这种结构:fn1(fn2(fn3(...args))),然后后再传入store.dispatch调用它。这里store.dispatch对应thunk的第二个参数next,此时每个thunk返回的内容为如下函数。

根据fn1(fn2(fn3(...args)))这个调用顺序,及最后一个fn3这个thunk返回的函数会作为前一个fn2的next入参。

这样调用完成后,会最终返回一个如下函数:

action => {
    if (xxx) {
        dispatch(xxx);
        return;
    }
    // 这里的next就是后一个thunk返回的action => {}这个函数
    return next(action);
};

到此,给redux加入thunk已经实现,这里的thunk可以具备各种功能,如异步thunk:

const thunk = ({ getState, dispatch }) => next => action => {
    if (typeof action === 'function') {
        return action(dispatch, getState);
    }
    return next(action);
};

然后只需要在页面调用时传入thunk:

const store = createStore(reducer, applyMiddleware(thunk));

3、combineReducers

有时我们项目比较大时,会将reducer分为多个文件编写,最后通过combineReducers组合多个reducer。下面是一个使用示例:

import user from './reducer/user.js';
import order from './reducer/order.js';

const reducer = combineReducers({
  user,
  order,
});

const store = createStore(reducer);

后代组件通过state.user或者state.order来获取对应大状态值。

接着让我们看下combineReducers长什么样子:

export function combineReducers(reducers) {
    const reducerKeys = Object.keys(reducers);
    const finalReducers = {};
    reducerKeys.forEach(key => {
        // 过滤不是function的reducer
        if (typeof reducerKeys[key] === 'function') {
            finalReducers[key] = reducerKeys[key];
        }
    });
    // 过滤后最终的finalReducers
    const finalReducerKeys = Object.keys(finalReducers);

    // 最终返回的reducer
    return (state, action) => {
        let hasChanged = false;
        const nextState = {};
        for (let i = 0; i < finalReducerKeys.length; i++) {
            let key = finalReducerKeys[i];
            let reducer = finalReducers[key];
            let prevStateForKey = state[key];
            // 对每一个reducer都执行action,并比较state是否changed
            let nextStateForKey = reducer(prevState, action);
            if (typeof nextStateForKey === 'undefined') {
                throw new Error(`reducer "${key}" returned undefined`);
            }
            nextState[key] = nextStateForKey;
            hasChanged = hasChanged || prevStateForKey !== nextStateForKey;
        }
        hasChanged =
            hasChanged || finalReducerKeys.length !== Object.keys(state).length;
        return hasChanged ? nextState : state;
    };
}

小结

这片文章对redux中几个常用的函数进行简单的讲解,源代码中会比这篇文章的代码多很多情况的处理。

希望这篇文章对你有所帮助

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值