Redux 原理与实现

redux 工作原理

ReduxReact 之间并没有什么关系,脱离了 ReactRedux 也可以与其它的 js 库(甚至是原生 js)搭配使用,Redux 只是一个状态管理库,但它与 React 搭配时却很好用,使开发 React 应用更加简介。而使用 Redux 库时,需要先做“配置”,因为这些代码的书写是必不可少的。下面的图是 redux 的工作流:

 

首先,react 组件从 store 中获取原始的数据,然后渲染。当 react 中的数据发生改变时,react 就需要使用 action,让 action 携带新的数据值派发给 store,store 把 action 发给 reducer 函数,reducer 函数处理新的数据然后返回给 store,最后 react 组件拿到更新后的数据渲染页面,达到页面更新的目的。

需要注意的是,在使用 Redux 时,最好不要监视最外层的容器,这样会把整个页面重新渲染,这是很浪费的,你应该绑定到像 App 这样的容器组件中。然后在容器组件中通过 props 向展示组件传递数据。

实现 Redux

首先捋一下思路,Redux 库中都有哪些函数?这些函数的参数都有哪些?参数类型是什么?执行函数后会返回什么?下面就一一介绍一下 redux 中的函数,当然在实际的 redux 源码中要复杂一些,不过在这篇文章中核心概念是一样的。

1. createStore

该函数会创建一个 store,专门用于存储数据。他返回四个函数:

  • dispatch:用于派发 action;
  • getState:用于获得 store 中的数据;
  • subscribe:订阅函数,当 state 数据改变后,就会触发监听函数;
  • replaceReducer:reducer 增强器;

createStore 可以接收三个参数:

  • reducer - 我们自己写的 reducer 函数;
  • preloadedState - 可选参数,表示默认的 state 值,没有这个参数时,enhancer 可以是 createStore 的第二个参数;
  • enhancer - 可选参数,增强器,比如 applyMiddleware 就是一个 enhancer;

该函数的模样:

function createStore(reducer,preloadedState,enhancer){
  let state;

  //  用于存放被 subscribe 订阅的函数(监听函数)
  let listeners = [];

  // getState 是一个很简单的函数
  const getState = () => state;

  return {
    dispatch,
    getState,
    subscribe,
    replaceReducer
  }
}

那就来一一实现各个“子函数”。

dispatch

该函数是派发 action 的,因此会接受一个 action 对象作为参数:

function dispatch(action) {
  // 通过 reducer 返回新的 state
  // 这个 reducer 就是 createStore 函数的第一个参数
  state = reducer(state, action);
  // 每一次状态更新后,都需要调用 listeners 数组中的每一个监听函数
  listeners.forEach(listener => listener());
  return action;    // 返回 action
}

subscribe

这个函数主要是往 listeners 数组中放入监听函数,参数就是一个监听函数。它还会返回一个 unsubscribe 函数,用于取消当前的订阅。

function subscribe(listener){
  listeners.push(listener);
  // 函数取消订阅函数
  return () => {
    listeners = listeners.filter(fn => fn !== listener);
  }
}

replaceReducer

顾名思义,这个函数可以替换 reducer,它传入一个 reducer 用以替代当前执行的 reducer 函数。

function replaceReducer(reducer){
  if (typeof nextReducer !== 'function') {
    throw new Error('Expected the nextReducer to be a function.')
  }

  currentReducer = nextReducer;
}

需要注意的是,在源码中完成负值后还会再派发一个类型为 @@redux/INIT 的 action。

2. combineReducers

该函数接收一个对象参数,对象的值是小的 reducer 函数。combineReducers 函数会返回总的 reducer 函数。combineReducers 函数样子:

function combineReducers(reducers){
  // 返回总的 reducer 函数,
  // 与小的 reducer 函数功能一样,返回更新后的 state
  return (state = {},action) => {
    // ...
  }
}

调用 combineReducers 函数:

import { combineReducers, createStore } from "redux";
import reducer1 from "./reducer1";
import reducer2 from "./reducer2";
// rootReducer 是一个新的 reducer 函数
const rootReducer = combineReducers({
  reducer1,
  reducer2
});
var store = createStore(rootReducer);

具体实现:

function combineReducers(reducers){
    return (state = {},action) => {
        // 返回的是一个对象,reducer 就是返回的对象
        return Object.keys(reducers).reduce(
            (accum,currentKey) => {
                accum[currentKey] = reducers[currentKey](state[currentKey],action);
                return accum;
            },{}        // accum 初始值是空对象
        );
    }
}

3. 写一个中间件

redux-thunk

redux-thunk 实现起来就更简单了,先回顾一下 redux-thunk 的使用方式,要想用 dispatch 派发异步请求来的数据需要在定义一个函数,该函数返回一个函数,参数是 dispatch:

// actions.js

const ajaxDataAction = (data) => ({ type: "ajax_data", payload: data });

export function ajaxAction(){
  // 这个 action 会返回一个函数
  return (dispatch) => {
    fetch("/info").then(json => json())
    .then(data => {
      // 得到数据后派发 action
      dispatch(ajaxDataAction(data));
    });
  }
}

因此,redux-thunk 函数内部需要先拦截 dispatch 函数,判断 action 参数的数据类型是不是函数,如果是函数就执行函数:

function thunk({ getState, dispatch }){

  return  next =>  action => {
    if(typeof action === "function"){
      return action(dispatch, getState);
    }
    // 传递给下一个中间件
    return next(action);
  }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值