27 行代码实现 Redux

2025-05-26

27 行代码实现 Redux

Redux 已经成为 React 中状态管理的事实标准。它是处理全局状态的绝佳工具,其如此受欢迎意味着你可能以后会想学习它。

然而,Redux 并非最容易学习的概念。尽管它的文档很完善(并且正在重写,力求更上一层楼),但 Redux 的单向数据流、调度、reducing、action 等等概念通常很难理解。我第一次接触 Redux 时,就为此苦苦挣扎。

幸运的是,Redux 并没有看起来那么复杂。事实上,你只需 27 行代码就能实现 Redux 核心部分的工作版本!

本文将指导您如何自己实现类似于 Redux 的 API。并非因为您只想这样做,而是因为它可以帮助您理解 Redux 的工作原理!

Redux 到底是什么?🤔

Redux 的核心部分是 store。这个 store 包含一棵状态树。store 允许你读取状态、dispatch action 来更新状态、订阅和取消订阅该状态的更新,仅此而已。

此存储会在您的应用程序中传递。如果您使用的是 React,则可能会将存储传递给react-redux<Provider />组件,这样您就可以通过使用 包装组件,在应用程序的其他部分访问它connect()

让我们实现 Redux!

我们将通过实现该createStore方法来重新实现 Redux。它的作用正如其名——它为我们提供了一个可用的 store 实例。这个 store 只是一个带有一些方法的对象,所以它不需要太复杂。

步骤 1:getState

让我们从小事做起,实现以下getState方法:

function createStore() {
  let state = {};
  return {
    getState() {
      return state;
    }
  };
}

当我们调用 时createStore,我们会创建一个空的状态对象。这就是你一直听说的那棵单状态树。我们返回我们的“store”,它只是一个带有一个属性的对象——一个getState函数。调用此getState函数可以访问闭包state内部的变量createStore

我们的使用方法如下:

import { createStore } from './redux';

const store = createStore();
const state = store.getState();

步骤 2:接受 Reducer

Redux 的核心概念之一是 Reducer。Redux Reducer 是一个函数,它接受当前状态和一个 Action,并返回下一个状态(即 Action 发生后的状态)。以下是一个简单的示例:

function countReducer(state = 0, action) {
  if (action.type === 'INCREMENT') return state + 1;
  if (action.type === 'DECREMENT') return state - 1;
  return state;
}

这里 -countReducer响应两个动作 -INCREMENTDECREMENT。如果传递的动作也不匹配,则返回当前状态。

为了继续理解 Redux,我们需要稍事休息,并了解 Redux 的数据流:

  1. 用户发起一个动作
  2. 该动作被传递给你的reducer
  3. Reducer 返回新的状态
  4. 状态已在商店中更新
  5. 任何对新状态感兴趣的人都会收到通知。

为了遵循这个流程,我们需要我们的 store 有一个 reducer!我们把它作为第一个参数传入:

function createStore(initialReducer) {
  let reducer = initialReducer;
  let state = reducer({}, { type: '__INIT__' });
  return {
    getState() {
      return state;
    }
  };
}

这里,我们接受一个 Reducer,并调用它来获取初始状态。我们“触发”一个初始动作,并将一个空对象传递给状态。

Redux 实际上允许我们在创建 store 时传入预先计算的状态。这些状态可能持久化在本地存储中,也可能来自服务端。总之,添加对它的支持非常简单,只需将initialState参数传递给createStore函数即可:

function createStore(initialReducer, initialState = {}) {
  let reducer = initialReducer;
  let state = reducer(initialState, { type: '__INIT__' });
  return {
    getState() {
      return state;
    }
  };
}

太棒了!现在我们甚至支持服务器端渲染了——太棒了!

步骤 3:派遣行动!

Redux 之旅的下一步是让用户能够通过某种方式来告知应用中发生了什么。Redux 通过一个dispatch函数解决了这个问题,该函数允许我们通过 action 调用 Reducer。

function createStore(initialReducer, initialState = {}) {
  let reducer = initialReducer;
  let state = reducer(initialState, { type: '__INIT__' });
  return {
    getState() {
      return state;
    },
    dispatch(action) {
      state = reducer(state, action);
    }
  };
}

从实现中我们可以看出,“调度” action 的概念就是使用当前状态和传入的 action 调用 reducer 函数。看起来相当简单!

步骤 4:订阅更改

如果我们不知道状态何时发生改变,那么改变状态就毫无意义。因此,Redux 实现了一个简单的订阅模型。你可以调用该store.subscribe函数,并传入一个用于处理状态改变的处理程序,如下所示:

const store = createStore(reducer);
store.subscribe(() => console.log('The state changed! 💥', store.getState()));

让我们来实现它:

function createStore(initialReducer, initialState = {}) {
  let reducer = initialReducer;
  let subscribers = [];
  let state = reducer(initialState, { type: '__INIT__' });
  return {
    getState() {
      return state;
    },
    dispatch(action) {
      state = reducer(state, action);
      subscribers.forEach(subscriber => subscriber());
    },
    subscribe(listener) {
      subscribers.push(listener);
    }
  };
}

我们创建一个订阅者数组,初始为空。每当我们调用subscribe函数时,监听器就会被添加到列表中。最后,当我们 dispatch 一个 action 时,我们会调用所有订阅者,通知它们状态已更改。

步骤 5:取消订阅更改

Redux 还允许我们取消订阅状态更新。每当调用该subscribe函数时,都会返回一个取消订阅函数。当您想要取消订阅时,可以调用该函数。我们可以扩展该subscribe方法以返回此unsubscribe函数:

function createStore(initialReducer, initialState = {}) {
  let reducer = initialReducer;
  let subscribers = [];
  let state = reducer(initialState, { type: '__INIT__' });
  return {
    getState() {
      return state;
    },
    dispatch(action) {
      state = reducer(state, action);
      subscribers.forEach(subscriber => subscriber());
    },
    subscribe(listener) {
      subscribers.push(listener);
      return () => {
        subscribers = subscribers.filter(subscriber => subscriber !== listener);
      };
    }
  };
}

unsubscribe函数从内部订阅者注册表数组中移除订阅者。就这么简单。

步骤 6:更换减速器

如果你动态加载应用程序的某些部分,则可能需要更新 reducer 函数。这不太常见,但由于它是 store API 的最后一部分,所以我们还是实现对它的支持吧:

function createStore(initialReducer, initialState = {}) {
  let reducer = initialReducer;
  let subscribers = [];
  let state = reducer(initialState, { type: '__INIT__' });
  return {
    getState() {
      return state;
    },
    dispatch(action) {
      state = reducer(state, action);
      subscribers.forEach(subscriber => subscriber(state));
    },
    subscribe(listener) {
      subscribers.push(listener);
      return () => {
        subscribers = subscribers.filter(subscriber => subscriber !== listener);
      };
    },
    replaceReducer(newReducer) {
      reducer = newReducer;
      this.dispatch({ type: '__REPLACE__' });
    }
  };
}

在这里,我们只需将旧的 Reducer 与新的 Reducer 交换,并分派一个动作来使用新的 Reducer 重新创建状态,以防我们的应用程序需要做一些特殊的响应。

第七步:商店增强器怎么样?

实际上,我们遗漏了实现中一个非常重要的部分——store 增强器。st​​ore 增强器是一个函数,它接受我们的createStore函数作为参数,并返回其增强版本。Redux 只附带一个增强器,即applyMiddleware,它让我们能够使用“中间件”的概念——让我们在dispatch方法调用之前和之后执行一些操作的函数。

实现对 store 增强器的支持只需三行代码。如果传入了其中一行,则调用它并返回再次调用的结果!

function createStore(initialReducer, initialState = {}, enhancer) {
  if (enhancer) {
    return enhancer(createStore)(initialReducer, initialState);
  }
  let reducer = initialReducer;
  let subscribers = [];
  let state = reducer(initialState, { type: '__INIT__' });
  return {
    getState() {
      return state;
    },
    dispatch(action) {
      state = reducer(state, action);
      subscribers.forEach(subscriber => subscriber(state));
    },
    subscribe(listener) {
      subscribers.push(listener);
      return () => {
        subscribers = subscribers.filter(subscriber => subscriber !== listener);
      };
    },
    replaceReducer(newReducer) {
      reducer = newReducer;
      this.dispatch({ type: '__REPLACE__' });
    }
  };
}

第 8 步?没有第 8 步!

就这样!你已经成功重建了 Redux 的核心部分!你可以把这 27 行代码放进你当前的应用中,它就能像原来一样正常工作了。

现在,您可能不应该这样做,因为 Redux 的实现方式为您提供了大量的安全措施、警告和速度优化,超过了上述实现 - 但它为您提供了相同的功能!

如果你想进一步了解 Redux 的实际工作原理,我建议你看一下实际的源代码。你会惊讶地发现它与我们刚刚编写的代码有多么相似。

总结

自己重新实现 Redux 真的没什么意义。充其量,这只是个有趣的派对小把戏。不过,看看它到底有多么神奇,希望能加深你对 Redux 工作原理的理解!毕竟,它不是一个神秘的黑盒子——它只是一些简单的方法和一个订阅模型。

希望本文能巩固你对 Redux 及其幕后工作原理的理解。如果你还有疑问,请在评论区留言,我会尽力解答!

文章来源:https://dev.to/selbekk/redux-in-27-lines-2i92
PREV
十条戒律
NEXT
2020 年如何开始使用 React