【问题标题】:React State Management: Context API as global storeReact 状态管理:上下文 API 作为全局存储
【发布时间】: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。

标签: reactjs typescript react-context


【解决方案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,
                });
            }
        }
    

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

    【讨论】:

      猜你喜欢
      • 2021-12-20
      • 2020-05-13
      • 1970-01-01
      • 2021-12-15
      • 2021-09-29
      • 1970-01-01
      • 2023-02-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多