banner
他山之石

他山之石

手摸手撸一个简单的Redux(一)

理解 Redux 的原理有助于我们更好的使用它。本文实现 Redux 的基本 API。

image

Redux 试图让 state 的变化变得可预测。

redux 已经被越来越多的人使用,理解其原理有助于更好的使用它。阅读 redux 源码是一个不错的办法,当我们了解了其原理之后,现在来实现一个简单的 redux 吧。

本文完整代码请查看 Github:https://github.com/YanYuanFE/redux-app

// clone repo
git clone https://github.com/YanYuanFE/redux-app.git


cd redux-app

// checkout branch
git checkout part-3

// install
npm install

// start
npm start

基本 API#

如果你使用过 redux,应该对 redux 的 API 了然于胸吧。redux 的基本 API 包括 createStore、getState、subscribe、dispatch。现在回顾一下 redux 的使用方法:

首先,redux 暴露出 createStore 方法,调用 createStore 方法传入 reducer,使用 const store = createStore (reducer),(此处暂时不考虑中间件),使用 store.getState () 获取状态,使用 store.dispatch () 发起一个 action,使用 store.subscribe () 订阅状态改变时执行的函数。下面是 redux API 的简单实现。

为了方便调试,使用前面讲的 redux 实现简单计数器的代码,在 src 目录下新建 redux.js,

export function createStore(reducer) {

}

在 redux.js 中,先导出 redux 的核心方法 createStore,传入 reducer 作为参数。
在 createStore 方法中,需要定义用于保存当前状态的变量以及用于保存状态改变后执行函数的监听器。

let currentState;
let currentListeners = [];

此处定义 currentState 为当前状态,初始化为 undefined; 定义 currentListenners 为监听器,初始化为数组。

然后,定义 getState 方法,用于获取当前状态,直接返回 currentState:

function getState() {
  return currentState;
}

然后,定义 subscribe 函数,用于订阅状态改变时执行的方法:

function subscribe(listener) {
  currentListeners.push(listener);
}

subscribe 方法传入一个监听函数,将监听函数 push 进监听器数组中。
然后定义 dispatch 方法,用于发起 action:

function dispatch(action) {
  currentState = reducer(currentState, action);
  currentListeners.forEach(v => v());
  return action;
}

dispatch 方法传入 action,然后调用 reducer 开始更新 currentState,传入当前 currentState 和 action,当状态改变时,通知监听器,监听器数组依次执行数组中的每一个订阅方法,此处使用了设计模式中的发布 —— 订阅模式,然后返回 action。

可以看到,上面已经实现了 Redux 的基本 API,那么就结束了吗?当然没有,因为 redux 并没有初始化,reducer 中的初始状态并没有生效,所以需要手动发起一个 action,并且 action.type 必须是独一无二的。如下:

dispatch({type: '@@REDUX/INIT'});

此处,对 redux 进行初始化,定义 type 为 '@@REDUX/INIT',这样定义的原因是保证命中 reducer 中 action.type 为 default 使其返回初始化 state。
最后,根据 redux 使用方法可以知道,store 肯定是一个对象,对象包含 getState、subscribe、dispatch 等方法,所有,最后需要将上述方法返回。

return { getState, subscribe, dispatch };

至此,一个超级简单的 redux 就实现了,麻雀虽小,五脏俱全。这个 redux 虽然简单,省去了许多错误处理过程,但是对于理解 redux 足矣。下面是完整代码:

export function createStore(reducer) {
  let currentState;

  let currentListeners = [];

  function getState() {
    return currentState;
  }
  function subscribe(listener) {
    currentListeners.push(listener);
  }
  function dispatch(action) {
    currentState = reducer(currentState, action);
    currentListeners.forEach(v => v());
    return action;
  }

  dispatch({type: '@@REDUX/INIT'}); //初始化
  return { getState, subscribe, dispatch }
}

测试#

下面结合之前的计数器例子来验证上述 redux 是否正确。
在前面计数器例子中,打开 src 下 index.js,修改如下代码:

import { createStore } from './redux';

替换 redux 为刚刚编写的 redux.js 文件。查看浏览器运行结果。
image

如图,与 redux 的效果一致,达到预期效果。

总结#

本文实现了一个简单的 redux,完成其基本 API 的实现,这有助于你理解 redux 的原理,后面会逐步对其扩展,编写 react-redux 以及中间件的实现。

加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。