前端圈

分享与交流前端开发相关知识

使用React Hooks&Context API构建Redux样式状态容器

状态管理很难

对于我们大多数人来说,在复杂的React应用程序中很难正确地进行状态管理。状态可以包括UI状态,例如路由,表单状态,分页,选定的选项卡等,以及来自http调用的响应,加载状态,缓存的数据等。

即使在Facebook,他们也难以显示聊天消息的正确通知计数。

应对日益增加的复杂性的必要性引起了一些有趣的库和范例。

一些流行的状态管理库:

Redux可能是与React串联使用的最受欢迎的单个库。它普及了单向数据流的概念,并使状态更新可预测和可管理。

我们将尝试构建一个具有相同原理的实用程序,即具有单向数据流的单一事实来源,其中通过调度动作(纯函数)执行状态更新。

上下文API

上下文提供了一种通过组件树传递数据的方法,而不必
在每个级别手动传递道具。

上下文是一个强大的工具。实际上,React的Redux绑定
本身使用的是ContextAPI。除了useReduceruseContext钩子,我们还拥有构建状态管理实用程序的所有组件。

演示时间

我们将构建一个带有2个按钮的基本计数器,以增加和减少计数。我们的全球商店将只有一个状态称为count。该演示将使用Typescript。

建立全球商店和减速机

首先让我们创建上下文对象。它具有状态对象本身和调度功能的两个属性。

// ...

const GlobalStateContext = createContextcreateContext<{
  state: State;
  dispatch: (action: Action) => void;
}>({ state: INITIAL_STATE, dispatch: () => {} });

// ...

当React渲染一个订阅该Context对象的组件时,它将从树中它上面最接近的匹配Provider读取当前上下文值。

reducer函数与Redux reducer完全相同,后者对传入的Action执行状态更新,然后返回新状态。

全部放在一起。

import { createContext, Reducer } from 'react';
import { ActionTypes } from './globalActions';

interface State {
  count: number;
}

export const INITIAL_STATE: State = {
  count: 0
};

export interface Action {
  type: ActionTypes;
  payload?: any;
}

export const GlobalStateContext = createContext<{
  state: State;
  dispatch: (action: Action) => void;
}>({ state: INITIAL_STATE, dispatch: () => {} });

export const globalReducer: Reducer<State, Action> = (state, action) => {
  const { type } = action;
  switch (type) {
    case ActionTypes.INCREMENT:
      return { ...state, count: state.count + 1 };
    case ActionTypes.DECREMENT:
      return { ...state, count: state.count - 1 };
    default:
      return state;
  }
};

我们有2个动作INCREMENTDECREMENT以及相应的动作创建者,它们会调度这些动作。

export enum ActionTypes {
  INCREMENT = 'INCREMENT',
  DECREMENT = 'DECREMENT'
}

export const incrementAction = () => ({
  type: ActionTypes.INCREMENT
});

export const decrementAction = () => ({
  type: ActionTypes.DECREMENT
});

将商店连接到组件

每个Context对象都带有一个ProviderReact组件,该组件允许使用组件订阅上下文更改。它接收作为value该提供者的后代的道具消耗组件。

useReducer是一个接受reducer和初始状态并返回与调度方法配对的当前状态的钩子。(如果您熟悉Redux,则已经知道它的工作原理。)

我们需要将应用程序的根组件包装在中Provider,并传递返回状态并作为valueprop进行分派。

// ...

const [globalState, dispatchToGlobal] = React.useReducer(
  globalReducer,
  INITIAL_STATE
);

return (
  <GlobalStateContext.Provider
    value={{ state: globalState, dispatch: dispatchToGlobal }}
  >
    <div className='App'>
      <Layout />
    </div>
  </GlobalStateContext.Provider>
);

// ...

此时,我们的整个应用程序都可以访问全局状态,并且可以将操作调度到商店。现在,将UI组件连接到商店。

所述useContext钩接受Context对象并返回该方面,在我们的情况下是当前上下文值statedispatch方法。

import * as React from 'react';
import { GlobalStateContext } from './context/globalStore';
import { incrementAction, decrementAction } from './context/globalActions';

const Layout: React.FC = () => {
  const { state, dispatch } = React.useContext(GlobalStateContext);

  return (
    <div>
      <div>
        <h2>Count : {state.count}</h2>
      </div>
      <div>
        <button onClick={() => dispatch(incrementAction())}>Increment</button>
        <button onClick={() => dispatch(decrementAction())}>Decrement</button>
      </div>
    </div>
  );
};

export default Layout;

源代码

CodeSandbox处签出完整源代码

结论

我们在本文中创建的状态管理实用程序展示了React Hooks和Context API的可能。这种方法最适合需要Redux等全局状态容器但不需要像中间件,调试扩展之类的所有麻烦的中小型应用程序。对于复杂的应用程序,我仍然使用Redux,您也应该尝试一下。

点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注