【问题标题】:React createContext issue in Typescript?在 Typescript 中反应 createContext 问题?
【发布时间】:2019-07-01 20:05:19
【问题描述】:

所以我在使用 React Context + Typescript 时遇到了一个非常奇怪的问题。

Working example

在上面的示例中,您可以看到我正在尝试做的实际工作。本质上,我正在使用新的 useContext 方法管理状态,并且它运行良好。

但是,当我尝试在我的盒子上执行此操作时,它似乎无法找到通过 useReducer 传递的状态值。

export function AdminStoreProvider(props: any) {
const [state, dispatch] = useReducer(reducer, initialState);
// state.isAuth is avail here
// state.user is avail here
const value = { state, dispatch };
// value.state.isAuth is avail here
return (
    /* value does not contain state once applied to the value prop */
    <AdminStore.Provider value={value}>{props.children} 
    </AdminStore.Provider>
   );
}

错误信息:

Type '{ state: { isAuth: boolean; user: string; }; dispatch: 
Dispatch<Actions>; }' is missing the following properties from type 
'IState': isAuth, user

请记住,我使用的代码正是我在盒子上使用的代码,我什至从沙盒下载了代码并尝试运行它,但它不起作用。

我正在使用 VSCode 1.31

我已经成功推断出,如果我改变创建上下文的方式:

export const AdminStore = React.createContext(initialState);

export const AdminStore = React.createContext(null);

value 属性不再抛出该错误。

但是,现在 useContext 返回错误:状态不存在于 null 上。如果我将 context 的 defaultState 设置为 {},也是如此。

当然,如果我

React.createContext();  

然后 TS 大喊没有提供 defaultValue。

在沙盒中,创建上下文对象的所有 3 个版本都可以正常工作。

提前感谢您的任何建议。

【问题讨论】:

    标签: reactjs typescript react-hooks react-context


    【解决方案1】:

    看来React.createContextdefaultValue 值应为以下类型:

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

    一旦为此类型创建了Context 对象,例如:

    export const AdminStore = React.createContext({} as IContextProps);
    

    Provider React 组件不应再抱怨该错误。

    以下是更改列表:

    admin-store.tsx

    import React, { useReducer } from "react";
    import { initialState, IState, reducer } from "./reducer";
    
    
    interface IContextProps {
      state: IState;
      dispatch: ({type}:{type:string}) => void;
    }
    
    
    export const AdminStore = React.createContext({} as IContextProps);
    
    export function AdminStoreProvider(props: any) {
      const [state, dispatch] = useReducer(reducer, initialState);
    
      const value = { state, dispatch };
      return (
        <AdminStore.Provider value={value}>{props.children}</AdminStore.Provider>
      );
    }
    

    【讨论】:

    • 谢谢,这确实消除了错误,但现在出现错误:Type '{ state: { isAuth: boolean; user: string; }; dispatch: Dispatch&lt;Actions&gt;; }' is not assignable to type 'IContextProps'. Types of property 'dispatch' are incompatible. Type 'Dispatch&lt;Actions&gt;' is not assignable to type '({ type }: { type: string; }) =&gt; void'. Types of parameters 'value' and '__0' are incompatible. Type '{ type: string; }' is not assignable to type 'Actions'. Property 'value' is missing in type '{ type: string; }' but required in type 'ILogout'
    • 这是我最新的:codesandbox.io/s/7jk69315l0 它现在实际上可以编译,但在浏览器中崩溃:export function AdminStoreProvider(props: any) { 说“TypeError: Object(...) is not a function”
    • 旁注,一旦我从 react 导入 Dispatch,然后导出和导入 Actions,我的主要问题通过警告和错误得到解决。 interface IContextProps { state: IState; dispatch: Dispatch&lt;Actions&gt;; } 是我最终的工作界面
    • 这对我不起作用。我收到错误:Type assertion on object literals is forbidden, use a type annotation instead.
    • @bauervision 答案是否完整,或者是否有人需要添加您评论中的代码(2 月 14 日 19:38)才能使其正常工作?如果是这样,您能否相应地更新答案,以便接受的答案包含正确的代码?
    【解决方案2】:

    我玩得很开心,所以我想我会分享我的想法。

    SidebarProps 表示上下文的状态。除了 reducer 操作之外,其他所有内容基本上都可以按原样使用。

    这是一篇很好的文章,解释了完全相同的解决方法(不在 TypeScript 中):Mixing Hooks and Context Api

    import React, { createContext, Dispatch, Reducer, useContext, useReducer } from 'react';
    
    interface Actions {
        type: string;
        value: any;
    }
    
    interface SidebarProps {
        show: boolean;
        content: JSX.Element | null;
    }
    
    interface SidebarProviderProps {
        reducer: Reducer<SidebarProps, Actions>;
        initState: SidebarProps;
    }
    
    interface InitContextProps {
        state: SidebarProps;
        dispatch: Dispatch<Actions>;
    }
    
    export const SidebarContext = createContext({} as InitContextProps);
    export const SidebarProvider: React.FC<SidebarProviderProps> = ({ reducer, initState, children }) => {
        const [state, dispatch] = useReducer(reducer, initState);
        const value = { state, dispatch };
        return (
            <SidebarContext.Provider value={value}>
                {children}
            </SidebarContext.Provider>
        );
    };
    export const useSidebar = () => useContext(SidebarContext);
    
    const SidebarController: React.FC = ({ children }) => {
        const initState: SidebarProps = {
            show: false,
            content: null
        };
    
        const reducer: Reducer<SidebarProps, Actions> = (state, action) => {
            switch (action.type) {
                case 'setShow':
                    return {
                        ...state,
                        show: action.value
                    };
    
                case 'setContent':
                    return {
                        ...state,
                        content: action.value
                    };
    
                default:
                    return state;
            }
        };
    
        return (
            <SidebarProvider reducer={reducer} initState={initState}>
                {children}
            </SidebarProvider>
        );
    };
    
    export default SidebarController;
    

    【讨论】:

      猜你喜欢
      • 2022-01-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-15
      • 2021-07-08
      • 2019-08-07
      • 2020-11-14
      • 1970-01-01
      相关资源
      最近更新 更多