创建redux项目_重新创建redux

创建redux项目

I had a professor that once said; you can’t fully understand an algorithm until you can code it yourself. This is the first part of a series of articles where we will follow his wise (but maybe not completely true) advise. We will learn some of the most popular tools used in modern front end development by recreating them from scratch. Coming articles will include implementations of module bundling tools, testing frameworks and server side rendering.

我有位教授曾经说过; 您必须自己编写代码,才能完全理解算法。 这是系列文章的第一部分,我们将遵循他的明智建议(但可能并不完全正确)。 我们将从头开始重新创建它们,以了解现代前端开发中使用的一些最受欢迎的工具。 即将发表的文章将包括模块捆绑工具,测试框架和服务器端渲染的实现。

Redux is one of the most used, loved, and confusing libraries in modern frontend. It is something that most new React developers stumble upon and can have a hard time wrapping their head around. In this article I will try to explain how Redux works by creating our own lightweight implementation of it.

Redux是现代前端中最常用,最受欢迎和令人困惑的库之一。 这是大多数新的React开发人员偶然发现的事情,并且可能很难解决。 在本文中,我将尝试通过创建我们自己的轻量级实现来解释Redux的工作原理。

But first, what is Redux? According to the Redux docs:

但是首先,什么是Redux? 根据Redux文档:

“Redux is a predictable state container for JavaScript apps.”

“ Redux是JavaScript应用程序的可预测状态容器。”

In other words, redux helps you manage state for JavaScript web applications. It’s most commonly used together with React but can be used together with any framework like Vue, Angular or for vanilla applications without any framework at all.

换句话说,redux帮助您管理JavaScript Web应用程序的状态。 它最常与React一起使用,但可以与任何框架(例如Vue,Angular)一起使用,也可以用于没有框架的原始应用程序。

Illustrated above are the building blocks of Redux architecture; actions, reducer, store and view. Our custom implementation of Redux will follow the above pattern.

上图显示了Redux体系结构的构建基块。 动作减速器存储查看。 我们的Redux定制实现将遵循上述模式。

The application state is managed in the store. The state itself is just a plain JavaScript object. The store also provides methods to update the state and read from the state.

应用程序状态在store管理。 状态本身只是一个普通JavaScript对象。 存储还提供了更新状态和从状态读取的方法。

Redux is using a publish/subscribe (PubSub) pattern to handle state updates. When a user interacts with the UI, actions can be dispatched (published) to update the state. Views can listen (subscribe) to those updates and modify the UI accordingly. Actions in redux are plain JavaScript objects with a type attribute containing a unique key for the action and a payload attribute containing the data.

Redux使用发布/订阅(PubSub)模式来处理状态更新。 当用户与UI交互时,可以调度(发布)操作以更新状态。 视图可以收听(订阅)这些更新并相应地修改UI。 redux中的动作是普通JavaScript对象,其类型属性包含动作的唯一键,有效载荷属性包含数据。

Actions can’t update the state themselves, they simple tell the reducers what they should update. All updates are handled by reducers which are pure functions taking the state and action as inputs, applying the action to the state and returning the new state.

动作本身不能更新状态,它们简单地告诉化简器应该更新什么。 所有更新均由reducer处理,reduce是将状态和操作作为输入,将操作应用于状态并返回新状态的纯函数。

Built upon this concepts are the three core principles of Redux:

基于此概念的是Redux的三项核心原则:

真理的单一来源 (Single source of truth)

The state of your whole application is stored in an object tree within a single store.

整个应用程序的状态存储在单个存储中的对象树中。

状态为只读 (State is read-only)

The only way to change the state is to emit an action, an object describing what happened.

更改状态的唯一方法是发出一个动作,一个描述发生了什么的对象。

使用纯函数进行更改 (Changes are made with pure functions)

To specify how the state tree is transformed by actions, we write pure reducers (pure = functions that returns a given output for every given input).

为了指定状态树如何通过动作进行转换,我们编写了纯约简器(pure =函数,它为每个给定输入返回一个给定输出)。

It might seem complex at first but after creating or own implementation of redux I hope everything will be a bit more clear. Now, let’s apply what we’ve learned in code!

乍一看似乎很复杂,但是在创建或自己实现redux之后,我希望一切都会更加清楚。 现在,让我们应用在代码中学到的知识!

创建Redux (Creating Redux)

We will start off with the full implementation of our Redux store and then break it down into pieces. I think this might come as a surprise for many (it certainly did for me), but we can implement a fully working redux store with 16 lines of code (!).

我们将从Redux存储的完整实现开始,然后将其分解。 我认为这可能会让许多人感到意外(这确实对我来说是成功的),但是我们可以使用16行代码(!)来实现完全正常工作的redux存储。

const createStore = (reducer, initialState) => {
  const store = {
    state: initialState,
    listeners: [],
    getState: () => store.state,
    subscribe: (listener) => {
      store.listeners.push(listener);
    },
    dispatch: (action) => {
      store.state = reducer(store.state, action);
      store.listeners.forEach((listener) => listener());
    }
  };


  return store;
};

If we simplify the above, the store looks something like this:

如果我们简化上述操作,那么商店看起来像这样:

const store = {
state: {}, // a plain object holding our state
listeners: [], // an array containing all listeners (subscribers)
getState: () => {}, // function to get the current state
dispatch: () => {}, // function to dispatch updates to the state
subscribe: () => {}, // subscribe function to add new listeners
}

In the full implementation you can see that everything is happening inside the function create store. This function takes two arguments; reducer and initialState. As mentioned before, reducers are the only ones allowed to change the state. They do so, not by mutating the state, but by returning a new state with the action applied to the previous state. The initialState argument simply lets us create a new store with some predefined data.

在完整的实现中,您可以看到在函数create store发生了所有事情。 该函数有两个参数。 reducerinitialState 。 如前所述,reducer是唯一允许更改状态的工具。 他们这样做不是通过改变状态,而是通过将新操作返回到先前状态而返回新状态。 initialState参数仅使我们可以使用一些预定义的数据来创建新商店。

In our implementation state, listeners, and getState are quite self-explaining. State is simply an object holding our state. Listeners is an array holding all listeners (subscribers) and getState is a function that returns the current state. The subscribe function adds new subscribers to the array of listeners. A subscriber is a function that will be executed every time the state is updated.

在我们的实现statelistenersgetState相当不言自明。 国家只是持有我们国家的对象。 侦听器是一个包含所有侦听器(订阅者)的数组,而getState是一个返回当前状态的函数。 订阅功能将新的订阅者添加到侦听器数组。 订户是每次状态更新时都会执行的功能。

That’s it for the store, if you remember our architecture we just need to add a reducer, actions and views to have a fully working implementation.

商店就是这样,如果您还记得我们的体系结构,我们只需要添加一个reducer,动作和视图即可实现完全正常的实现。

减速器及动作 (Reducer and actions)

It is in the reducer function that most of the magic happens. Reducer takes the previous state and an action as input (we already know that actions are objects containing a type and a payload). The reducer function will apply the corresponding changes for the action and return a new state. A reducer typically looks something like this:

大多数的魔术都是在化简函数中进行的。 Reducer将先前的状态和一个动作作为输入(我们已经知道动作是包含类型和有效负载的对象)。 reducer函数将对操作应用相应的更改并返回新状态。 减速器通常看起来像这样:

const reducer = (previousState, action) => {
  switch (action.type) {
    case 'ADD':
      const nextState = {
        number: previousState.number + action.payload.number,
      };


      return nextState;
    case 'SUBTRACT':
      const nextState = {
        number: previousState.number - action.payload.number,
      };


      return nextState;
    default:
      return state;
  }
};

In this example we have two types of actions to modify the state: ADD and SUBTRACT. If we dispatch an action with the type of “add” the reducer will add the number in the payload to the number in the current state. The reducer then returns the updated state. If we dispatch any other action than add or subtract, the reducer will fallback to the default and return the current state without modifications.

在此示例中,我们有两种类型的操作来修改状态: ADDSUBTRACT 。 如果我们以“添加”类型调度动作,则减速器会将有效负载中的数字添加到当前状态的数字中。 然后,reducer返回更新后的状态。 如果我们分派除加法或减法之外的任何其他动作,则减速器将回退至默认值并返回当前状态,而无需进行修改。

The second part of our dispatch function is the listeners. On every state change, we iterate through the array of listeners and execute each one. By doing this we make sure that all views that are subscribing can update accordingly as the state changes.

调度功能的第二部分是侦听器。 在每次状态更改时,我们都会遍历侦听器数组并执行每个侦听器。 通过这样做,我们确保所有订阅的视图都可以随着状态的改变而相应地更新。

store.listeners.forEach((listener) => listener());

That is all for our createStore function. Now we will do a full implementation containing all the building blocks; actions, reducers, store and views.

这就是我们的createStore函数。 现在,我们将进行包含所有构件的完整实现; 动作,减速器,商店视图。

视图 (View)

We have already created our store and reducer above. The view in our example is a counter that shows a number and have buttons to perform the actions of adding or subtracting from that number. We achieve this with some simple DOM-elements and a subscriber to our store.

我们已经在上面创建了商店和减速器。 本例中的视图是一个计数器,该计数器显示一个数字,并具有用于执行对该数字进行加法或减法操作的按钮。 我们通过一些简单的DOM元素和商店的订阅者来实现这一目标。

<div>
  Number: <span id="number"></span>
  <button id="add">ADD</button>
  <button id="subtract">SUBTRACT</button>
</div>
<script>
  // Create our store with the initial state and a reducer
  const initialState = {
    number: 1337
  }
  const store = createStore(reducer, initialState); // the reducer should be the function we created before


  // Create a listener that will update the number element if the state changes
  const numberNode = document.getElementById('number');
  store.subscribe(() => {
    const state = store.getState();
    const number = state.number;
    numberNode.innerHTML = number;
  });
</script>

And finally we have the actions and dispatch. This is also mapped to our view — if the user clicks the add or subtract button the corresponding action should be dispatched.

最后,我们有行动和调度。 这也映射到我们的视图-如果用户单击加或减按钮,则应调度相应的操作。

// Dispatch updates when clicking the add button
document.getElementById('add').addEventListener('click', () => {
  store.dispatch({
    type: 'ADD',
    payload: {
      number: 1,
    },
  });
});

All together we get something like this:

总之,我们得到的是这样的:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Redux example</title>
  </head>
  <body>
    <div>
      Number: <span id="number"></span>
      <button id="add">ADD</button>
      <button id="subtract">SUBTRACT</button>
    </div>
    <script>
      // Creates a redux store
      const createStore = (reducer, initialState) => {
        const store = {
          state: initialState,
          listeners: [],
          getState: () => store.state,
          subscribe: listener => {
            store.listeners.push(listener);
          },
          dispatch: action => {
            store.state = reducer(store.state, action);
            store.listeners.forEach(listener => listener());
          }
        };


        return store;
      };


      // Reducer that adds or subtracts numbers from the state
      const reducer = (previousState, action) => {
        switch (action.type) {
          case 'ADD':
            return {
              number: previousState.number + action.payload.number,
            };
          case 'SUBTRACT':
            return {
              number: previousState.number - action.payload.number,
            };
          default:
            return previousState;
        }
      };


      // Create our store with the initial state and a reducer
      const initialState = {
        number: 1337
      }
      const store = createStore(reducer, initialState);


      // Create a listener that will update the number element if the state changes
      const numberNode = document.getElementById('number');
      store.subscribe(() => {
        const state = store.getState();
        const number = state.number;
        numberNode.innerHTML = number;
      });


      // Dispatch updates when clicking the add button
      document.getElementById('add').addEventListener('click', () => {
        store.dispatch({
          type: 'ADD',
          payload: {
            number: 1,
          },
        });
      });
      
      // Dispatch updates when clicking the subtract button
      document.getElementById('subtract').addEventListener('click', () => {
        store.dispatch({
          type: 'SUBTRACT',
          payload: {
            number: 1,
          },
        });
      });


      // Run this to initialize the state (otherwise the number node will be empty on load)
      store.dispatch({})
    </script>
  </body>
</html>

This is not a production ready implementation but it can get you quite far. The redux library covers a lot of edge cases and have a few more advanced features. Especially if you use its react wrapper that comes with hooks and other nice functionalities.

这不是生产就绪的实现,但是可以带给您很大的帮助。 redux库涵盖了许多边缘情况,并具有一些更高级的功能。 特别是如果您使用其带有钩子和其他不错功能的React包装器。

结语 (Wrap up)

Some tools and libraries that can seem daunting at first are actually quite simple when you break them down. Hopefully this article have given you a basic understanding how Redux works. When you feel ready it might be time to experiment with some of the more advanced features of redux or look into popular design patterns like duck and similar.

当您将它们分解时,乍看之下有些令人生畏的工具和库实际上很简单。 希望本文对Redux的工作原理有基本的了解。 当您准备就绪时,就可以尝试使用redux的一些更高级的功能,或者研究常见的设计模式,例如鸭子和类似设计。

翻译自: https://medium.com/knowit-development/recreating-redux-a28d4e47b76f

创建redux项目

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值