zoukankan      html  css  js  c++  java
  • 如何使用24行JavaScript代码实现Redux

    作者:Yazeed Bzadough
    译者:小维FE
    原文:freecodecamp

    为了保证文章的可读性,本文采用意译而非直译。

    90%的规约,10%的库。
    Redux是迄今为止创建的最重要的JavaScript库之一,灵感来源于以前的艺术比如FluxElm,Redux通过引入一个包含三个简单要点的可伸缩体系结构,使得JavaScript函数式编程成为可能。如果你是初次接触Redux,可以考虑先阅读官方文档

    1. Redux大多是规约

    考虑如下这个使用了Redux架构的简单的计数器应用。如果你想跳过的话可以直接查看Github Repo

    1.1 State存储在一棵树中

    该应用程序的状态看起来如下:

    const initialState = { count: 0 };
    

    1.2 Action声明状态更改

    根据Redux规约,我们不直接修改(突变)状态。

    // 在Redux应用中不要做如下操作
    state.count = 1;
    

    相反,我们创建在应用中用户可能用到的所有行为。

    const actions = {
      increment: { type: 'INCREMENT' },
      decrement: { type: 'DECREMENT' }
    };
    

    1.3 Reducer解释行为并更新状态

    在最后一个架构部分我们叫做Reduer,其作为一个纯函数,它基于以前的状态和行为返回状态的新副本。

    • 如果increment被触发,则增加state.count
    • 如果decrement被触发,则减少state.count
    const countReducer = (state = initialState, action) => {
      switch (action.type) {
        case actions.increment.type:
          return {
            count: state.count + 1
          };
    
        case actions.decrement.type:
          return {
            count: state.count - 1
          };
    
        default:
          return state;
      }
    };
    

    1.4 目前为止还没有Redux

    你注意到了吗?到目前为止我们甚至还没有接触到Redux库,我们仅仅只是创建了一些对象和函数,这就是为什么我称其为"大多是规约",90%的Redux应用其实并不需要Redux。

    2. 开始实现Redux

    要使用这种架构,我们必须要将它放入到一个store当中,我们将仅仅实现一个函数:createStore。使用方式如下:

    import { createStore } from 'redux'
    
    const store = createStore(countReducer);
    
    store.subscribe(() => {
      console.log(store.getState());
    });
    
    store.dispatch(actions.increment);
    // logs { count: 1 }
    
    store.dispatch(actions.increment);
    // logs { count: 2 }
    
    store.dispatch(actions.decrement);
    // logs { count: 1 }
    

    下面这是我们的初始化样板代码,我们需要一个监听器列表listeners和reducer提供的初始化状态。

    const createStore = (yourReducer) => {
        let listeners = [];
        let currentState = yourReducer(undefined, {});
    }
    

    无论何时某人订阅了我们的store,那么他将会被添加到listeners数组中。这是非常重要的,因为每次当某人在派发(dispatch)一个动作(action)的时候,所有的listeners都需要在此次事件循环中被通知到。调用yourReducer函数并传入一个undefined和一个空对象将会返回一个initialState,这个值也就是我们在调用store.getState()时的返回值。既然说到这里了,我们就来创建这个方法。

    2.1 store.getState()

    这个函数用于从store中返回最新的状态,当用户每次点击一个按钮的时候我们都需要最新的状态来更新我们的视图。

    const createStore = (yourReducer) => {
        let listeners = [];
        let currentState = yourReducer(undefined, {});
        
        return {
            getState: () => currentState
        };
    }
    

    2.2 store.dispatch()

    这个函数使用一个action作为其入参,并且将这个actioncurrentState反馈给yourReducer来获取一个新的状态,并且dispatch方法还会通知到每一个订阅了当前store的监听者。

    const createStore = (yourReducer) => {
      let listeners = [];
      let currentState = yourReducer(undefined, {});
    
      return {
        getState: () => currentState,
        dispatch: (action) => {
          currentState = yourReducer(currentState, action);
    
          listeners.forEach((listener) => {
            listener();
          });
        }
      };
    };
    

    2.3 store.subscribe(listener)

    这个方法使得你在当store接收到一个action的时候能够被通知到,可以在这里调用store.getState()来获取最新的状态并更新UI。

    const createStore = (yourReducer) => {
      let listeners = [];
      let currentState = yourReducer(undefined, {});
    
      return {
        getState: () => currentState,
        dispatch: (action) => {
          currentState = yourReducer(currentState, action);
    
          listeners.forEach((listener) => {
            listener();
          });
        },
        subscribe: (newListener) => {
          listeners.push(newListener);
    
          const unsubscribe = () => {
            listeners = listeners.filter((l) => l !== newListener);
          };
    
          return unsubscribe;
        }
      };
    };
    

    同时subscribe函数返回了另一个函数unsubscribe,这个函数允许你当不再对store的更新感兴趣的时候能够取消订阅。

    3. 整理代码

    现在我们添加按钮的逻辑,来看看最后的源代码:

    // 简化版createStore函数
    const createStore = (yourReducer) => {
      let listeners = [];
      let currentState = yourReducer(undefined, {});
    
      return {
        getState: () => currentState,
        dispatch: (action) => {
          currentState = yourReducer(currentState, action);
    
          listeners.forEach((listener) => {
            listener();
          });
        },
        subscribe: (newListener) => {
          listeners.push(newListener);
    
          const unsubscribe = () => {
            listeners = listeners.filter((l) => l !== newListener);
          };
    
          return unsubscribe;
        }
      };
    };
    
    // Redux的架构组成部分
    const initialState = { count: 0 };
    
    const actions = {
      increment: { type: 'INCREMENT' },
      decrement: { type: 'DECREMENT' }
    };
    
    const countReducer = (state = initialState, action) => {
      switch (action.type) {
        case actions.increment.type:
          return {
            count: state.count + 1
          };
    
        case actions.decrement.type:
          return {
            count: state.count - 1
          };
    
        default:
          return state;
      }
    };
    
    const store = createStore(countReducer);
    
    // DOM元素
    const incrementButton = document.querySelector('.increment');
    const decrementButton = document.querySelector('.decrement');
    
    // 给按钮添加点击事件
    incrementButton.addEventListener('click', () => {
      store.dispatch(actions.increment);
    });
    
    decrementButton.addEventListener('click', () => {
      store.dispatch(actions.decrement);
    });
    
    // 初始化UI视图
    const counterDisplay = document.querySelector('h1');
    counterDisplay.innerHTML = parseInt(initialState.count);
    
    // 派发动作的时候跟新UI
    store.subscribe(() => {
      const state = store.getState();
    
      counterDisplay.innerHTML = parseInt(state.count);
    });
    

    我们再次看看最后的视图效果:

    原文: https://www.freecodecamp.org/news/redux-in-24-lines-of-code/

    4. 交流

    本篇主要简单了解下Redux的三个架构组成部分以及如何实现一个简化版的Redux,对Redux能有进一步的了解,希望能和大家相互讨论技术,一起交流学习。

    文章已同步更新至Github博客,若觉文章尚可,欢迎前往star!

    你的一个点赞,值得让我付出更多的努力!

    逆境中成长,只有不断地学习,才能成为更好的自己,与君共勉!

  • 相关阅读:
    【BZOJ 2124】【CodeVS 1283】等差子序列
    【BZOJ 1036】【ZJOI 2008】树的统计Count
    【BZOJ 1901】【ZJU 2112】Dynamic Rankings
    【BZOJ 3924】【ZJOI 2015】幻想乡战略游戏
    【BZOJ 4103】【THUSC 2015】异或运算
    【BZOJ 4513】【SDOI 2016】储能表
    【HDU 3622】Bomb Game
    【BZOJ 3166】【HEOI 2013】Alo
    【BZOJ 3530】【SDOI 2014】数数
    【BZOJ 4567】【SCOI 2016】背单词
  • 原文地址:https://www.cnblogs.com/tangshiwei/p/11870517.html
Copyright © 2011-2022 走看看