美文网首页
React应用状态管理(一):理解Action、Reducer、

React应用状态管理(一):理解Action、Reducer、

作者: AizawaSayo | 来源:发表于2021-01-29 23:27 被阅读0次

    因为正在开发的是一个小项目,除了登录用户信息、全局配置没有其他需要集中管理的状态,就没打算用Redux。杀鸡焉用牛刀,也有尽量少依赖第三方的强迫症,不想整合redux,react-redux和redux-thunk三个新包。当然Redux有很多优点:已经很完善且生态强大、拥有清晰的规范体系和特定的设计约束、以及精挑细选的API。遵循这种状态管理方案的最佳实践,在大型团队项目中可以很好地帮助我们约定代码规则,减少成员出错和乱套的概率。Mobx则是面向对象思维的状态管理库,我还没有用过。
    这次我想试试靠React单打,一通百度有样学样用React Hook的useContext结合useReducer模拟了Redux的状态管理机制。实质就是一个可以处理同步/异步动作的函数,并根据动作函数更新状态的集中状态管理器。这边记录下摸索过程的心得体会。

    我决定又悄咪咪分个上下两篇:占坑还没写完orz React应用状态管理(二):用React Hook模拟实现Redux

    参考文章地址:使用Hooks代替ReduxReact Hooks 版实现 Redux

    状态管理三剑客:Action、Reducer、Store(不局限于Redux)

    1. Action

    Action 就是一个普通JS对象,作用除了描述对 store 数据的更改操作,更是一个把数据从应用传到store的可靠载体(数据有可能是服务器响应,用户输入等等)。它必须包含一个type字段表明要操作的类型。
    一般都是通过 store.dispatch() 把 action 传到 store。

    const ADD = 'ITEMS_ADD'
    
    { type: ADD,  payload: 'text' }
    

    在大型项目中,把type的值显式地定义成常量会更利于团队协作。

    Action 只做简单的参数传递和配置,最多做一些数据格式的转换,如把 date 转成 formatted string。尽量把数据处理逻辑放在reducer中。因为按照语意 action 就只是一个 identifier,reducer 才是负责逻辑处理的。

    Action 创建函数是生成 action 的方法,这样做更方便我们传递数据。
    就是像这样简单的返回一个 action:

    export function completeItem(id: number) {
      return {
        type: ITEMS.COMPLETE,
        payload: id
      };
    }
    

    然后在项目中把 action 创建函数的结果传给 dispatch() 方法即可发起一次 dispatch 过程。dispatch(completeItem(id))
    或者创建一个 被绑定的 action 创建函数 来自动 dispatch,再直接调用:
    const boundCompleteItem = id => dispatch(completeItem(id))
    boundCompleteItem(id);

    2. Reducer

    reducer 是一个接收旧 state 和 action,并返回新的 state 的函数。它指定了如何响应 actions 并发送到 store 。记住 actions 只是描述了有事情发生了这一事实,并没有描述应用如何更新 state。
    它与被传入 Array.prototype.reduce(reducer, ?initialValue) 里的回调函数属于相同的类型,这就是reducer命名的由来。

    /** arr.reduce(callback, [initialValue]) **/
    /* 第一个参数是回调函数: (prev, curr) => prev + curr * 
    与redux当中的reducer模型 (previousState, action) => newState 是不是非常相似呢 */
    var sum = [0,1,2,3,4].reduce((prev,curr) => prev + curr); // sum = 10
    

    保持 reducer 纯净非常重要。永远不要在 reducer 里调用非纯函数或有执行副作用的操作(如 API 请求和路由跳转)。
    不要直接修改 state 可以用对象展开运算符{ ...state, ...newState } 达到合并新旧数据并返回新指针对象的目的。

    一个reducer函数示例

    export const initialState = [];
    
    // reducer接收 旧state和 action 并返回新的state
    export default function items(state = initialState, action) {
      // 根据不同的action.type对state进行不同的操作
      switch (action.type) {
        case ITEMS.RESET: {
          return [];
        }
        case ITEMS.ADD: {
          return [
            ...state,
            {
              id: Date.now(),
              text: action.payload,
              completed: false,
            },
          ];
        }
        case ITEMS.COMPLETE: {
          return state.map((item) => {
            if (item.id === action.payload) {
              return { ...item, completed: !item.completed }
            }
            return item;
          });
        }
        default: {
          return state;
        }
      }
    }
    
    
    3. Store

    Store 只是有几个方法的JS对象。它把Action和Reducer联系到一起,用来维持应用所有的 state 树 。 要创建一个store,只需要把根部的 reducer 函数传递给 createStore。 改变 store 内 state 的惟一途径是对它 dispatch 一个 action
    它提供的几个主要方法:

    以下是对createStore源码的模拟:

    const createStore = (reducer: any)=>{
      let state: any; 
      let listeners:[] = [];
        
      // 用来返回当前的state
      const getState = () => state;
    
      // 根据action调用reducer返回新的state并触发listener
      const dispatch=(action: any)=>{
        state=reducer(state,action);
        listeners.forEach((listener: any) => listener());
      };
      /* 这里的subscribe有两个功能  
      * 调用 subscribe(listener) 会使用listeners.push(listener)注册一个listener  * 而调用 subscribe 的返回函数则会注销掉listener  */
      const subscribe = (listener: never) => {
        listeners.push(listener);
        return () => {
          listeners:[] = listeners.filter(l=>l!==listener);
        };
      };
      return{ getState, dispatch, subscribe };
    };
    

    创建store

    import rootReducer from './reducers'
    let store = createStore(rootReducer)
    

    最后贴一个用js简单实现的基础版redux。

    可以帮助我们捋清redux最基本的流程。异步代码非原创,出处: 浅谈Redux Action & Reducer & Store
    下篇接着讲包含异步Action的React Hook版实现。

    const store = {
      state: {}, // 全局唯一的state,内部变量,通过getState()获取
      listeners: [], // listeners,用来诸如视图更新的操作
      dispatch: () => {}, // 分发action
      subscribe: () => {}, // 用来订阅state变化
      getState: () => {}, // 获取state
    }
    
    const createStore = (reducer, initialState) => {
      // internal variables
      const store = {};
      store.state = initialState;
      store.listeners = [];
      
      // api-subscribe
      store.subscribe = (listener) => {
        store.listeners.push(listener);
        return () => {
          store.listener = store.listeners.filter(l => l !== listener);
        };
      };
      // api-dispatch
      // 根据 action 调用reducer返回新的state,并触发listener
      store.dispatch = (action) => { 
        store.state = reducer(store.state, action);
        store.listeners.forEach(listener => listener());
      };
      
      // api-getState
      store.getState = () => store.state;
      
      return store;
    };
    
    // reducer
    function counter(state = 0, action) {
      switch (action.type) {
      case 'INCREMENT':
        return state + 1
      case 'DECREMENT':
        return state - 1
      default:
        return state
      }
    }
    
    let store = createStore(counter)
    
    store.subscribe(() =>
      console.log(store.getState())
    )
    
    
    store.dispatch({ type: 'INCREMENT' })
    // 1
    store.dispatch({ type: 'INCREMENT' })
    // 2
    store.dispatch({ type: 'DECREMENT' })
    // 1
    

    相关文章

      网友评论

          本文标题:React应用状态管理(一):理解Action、Reducer、

          本文链接:https://www.haomeiwen.com/subject/vlsttltx.html