React 状态管理:上下文 API 作为全局存储

Posted

技术标签:

【中文标题】React 状态管理:上下文 API 作为全局存储【英文标题】:React State Management: Context API as global store 【发布时间】:2021-03-08 19:03:48 【问题描述】:

我正在使用 TypeScript 开发一个 React Web 应用程序。 我想使用 React Hooks 和 Context API 设置状态管理,发现这个很酷、简单且简短的 tutorial。

我按照教程进行操作,但我的编译器显示了几个错误。我认为这是基本的类型错误,我不明白(我更习惯 javascript 而不是 TypeScript)。

我的状态上下文:

import React,  createContext, useContext, useReducer  from 'react';

export interface IState 
  isAuth: boolean;
  user: string;


interface IContextProps 
  state: IState;
  dispatch: ( type :  type: string ) => void;


export const StateContext = createContext( as IContextProps);
export const StateProvider = ( reducer, initialState, children ) => (
  <StateContext.Provider value=useReducer(reducer, initialState)>
    children
  </StateContext.Provider>
);
export const useStateValue = () => useContext(StateContext);

我的状态减速器:

import  initialState  from './InitialState';

export const reducer = (state = initialState, action: any) => 
  switch (action.type) 
    case 'setObj':
      return 
        ...state,
        obj: action.objValue,
      ;
    default:
      return state;
  
;

我的初始状态:

import  emptyObj  from '../interfaces/IObj';

export const initialState = 
  obj: emptyObj,
;

我的应用:

import React from 'react';

import  StateProvider  from './utils/StateContext';

import  initialState  from './globals/constants/InitialState';
import  reducer  from './globals/constants/StateReducer';

function App() 
  return (
    <div className='App'>
      <header className='App-header'>
        <StateProvider initialState=initialState reducer=reducer>
          <p>Hello World</p>
        </StateProvider>
      </header>
    </div>
  );


export default App;

错误仅在状态上下文文件中: 1.) 状态上下文 第一个错误是状态上下文。在教程中它被定义为: export const StateContext = createContext(); 并且编译器会抛出一个错误。 如果我使用export const StateContext = createContext(undefined);,那么错误就消失了。 此外,如果我使用(如开头所述)export const StateContext = createContext( as IContextProps); 并添加接口 IState 和 IContextProps 那么它也可以工作。我想那部分现在很好。 2.) 状态提供者export const StateProvider = ( reducer, initialState, children ) =&gt; ... 编译器为 reducer、initialState 和 children 抛出相同的错误:Binding Element [reducer / initialState / children] 隐含任何类型。 3.) 值value=useReducer(reducer, initialState)&gt;... 编译器喊: 类型“[未知,DispatchWithoutAction]”不可分配给类型“IContextProps”.ts(2322) index.d.ts(335, 9):预期类型来自属性“值”,该属性在此处声明为类型“IntrinsicAttributes & ProviderProps”

提前感谢您的帮助。如果您需要更多信息,请告诉我。

【问题讨论】:

1) createContext 函数需要一个默认值,以防有人试图在提供程序之外使用它。正如您所指出的,这可能只是未定义或空对象,但这是一项要求。 2)这是 Typescript 的一部分(可以关闭),但它告诉你它不能推断 reducer、inititalState 和 children 的类型。您将不得不更改您的代码,以便它可以推断它们或手动输入它们。 3) useReducer 无法推断您的状态的类型(由未知数显示),解决此问题的方法可能是将您的初始状态键入 IState。 【参考方案1】:

问题可能是因为

&lt;StateContext.Provider value=useReducer(reducer, initialState)&gt;

useReducer 返回一个数组,Provider 需要一个对象。

const [state, dispatch] = useReducer(reducers, initialState);
return (
    <StateContext.Provider value= state, dispatch >
       children
    </StateContext.Provider>
);

我已经写了一篇关于你可以的文章

文章:State Management with React Hooks and Context API

运行示例:https://stackblitz.com/edit/reactjs-usecontext-usereducer-state-management

【讨论】:

【参考方案2】:

我最近遇到了同样的问题,我通过这种方式解决了它,我希望它对你也有帮助。我更新了您的代码,如下面的 sn-ps 所示,

export interface IState 
    user: any,
    theme: boolean,
    drawer: boolean,


export const initialState = 
    user: null,
    theme: false,
    drawer: false,


export enum actionTypes 
    SET_THEME = "SET_THEME",
    SET_USER = "SET_USER",
    OPEN_DRAWER = "OPEN_DRAWER",


export type IAction =
    | 
        type: actionTypes.SET_USER,
        value: any
     | 
        type: actionTypes.SET_THEME,
        value: boolean
    | 
        type: actionTypes.OPEN_DRAWER,
        value: boolean
    

const reducer = (state: IState, action:IAction): IState => 
    switch (action.type) 
        case actionTypes.SET_THEME:
            return 
                ...state,
                theme: action.value,
            
        case actionTypes.SET_USER:
            return 
                ...state,
                user: action.value,
            
        case actionTypes.OPEN_DRAWER:
            return 
                ...state,
                user: action.value,
            
        default:
            return state
    


export default reducer;

上下文提供者(StateProvider)应该更新为...

import React, useReducer, createContext, useContext, ReactElement from 'react';
import initialState, IState from "./Reducer";

const StateContext = createContext<IState | any>(initialState);

interface StateProps
    children: ReactElement,
    initialState: (IState | any),
    reducer: (IState | any),


export const StateProvider = (children,initialState, reducer : StateProps): ReactElement => 
    // const [state, dispatch] = useReducer(initialState, reducer);
    return(
        <StateContext.Provider value=useReducer(initialState, reducer)>
            children
        </StateContext.Provider>
    );


export const useStateValue = () => useContext(StateContext);

然后用上下文提供者 (StateProvider) 包装你的主应用

import HeadTag, Layout from "../components/components";
import reducer, initialState from "../provider/Reducer";
import StateProvider from "../provider/StateProvider";

function MyApp( Component, pageProps : AppProps) 
  return (
      <>
          <HeadTag />
          <StateProvider initialState=initialState reducer=reducer >
              <Layout>
                  <Component ...pageProps />
              </Layout>
          </StateProvider>
      </>
  );

export default MyApp;

在您的应用中使用它

/* data layer */
    const [user, theme, drawer, dispatch] = useReducer(reducer, initialState);


    /* switch between dark and light mode */
    const handleTheme = () => 
        if(theme)
            dispatch(
                type: actionTypes.SET_THEME,
                value: false,
            );
        else
            dispatch(
                type: actionTypes.SET_THEME,
                value: true,
            );
        
    

我希望这能解决你的问题。

【讨论】:

以上是关于React 状态管理:上下文 API 作为全局存储的主要内容,如果未能解决你的问题,请参考以下文章

React Context API,从子组件设置上下文状态,而不是将函数作为道具传递

使用上下文 API (React.js) 仅更改状态对象中的特定键

如何使用 React Context API 跨多个 Routes 拥有状态?

反应 SPA。用于处理登录状态的全局状态存储 API?

React - 从全局上下文中过滤获取的数据的问题

在 React 中,将获取的 Firebase 数据存储在本地还是全局状态?