在Typescript中反应createContext问题?

bau*_*ion 8 typescript reactjs react-context react-hooks

所以我在React Context + Typescript上遇到了一个很奇怪的问题。

工作实例

在上面的示例中,您可以看到我正在尝试做的实际工作。本质上,我正在使用新的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>
   );
}
Run Code Online (Sandbox Code Playgroud)

错误信息:

Type '{ state: { isAuth: boolean; user: string; }; dispatch: 
Dispatch<Actions>; }' is missing the following properties from type 
'IState': isAuth, user
Run Code Online (Sandbox Code Playgroud)

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

我正在使用VSCode 1.31

我设法推断出,如果我从以下位置更改创建上下文的方式:

export const AdminStore = React.createContext(initialState);
Run Code Online (Sandbox Code Playgroud)

export const AdminStore = React.createContext(null);
Run Code Online (Sandbox Code Playgroud)

value属性不再引发该错误。

但是,现在useContext返回一个错误:状态不存在为null。同样,如果我将context的defaultState设置为{}。

当然,如果我

React.createContext();  
Run Code Online (Sandbox Code Playgroud)

然后TS大喊大叫没有提供defaultValue。

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

在此先感谢您的任何建议。

Vad*_*hev 21

看来的defaultValueReact.createContext应为以下类型:

interface IContextProps {
  state: IState;
  dispatch: ({type}:{type:string}) => void;
}
Run Code Online (Sandbox Code Playgroud)

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

export const AdminStore = React.createContext({} as IContextProps);
Run Code Online (Sandbox Code Playgroud)

提供者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>
  );
}
Run Code Online (Sandbox Code Playgroud)

  • 旁注,一旦我从react导入Dispatch,然后导出和导入Actions,我的主要问题就被警告和错误解决了。接口IContextProps {state:IState; dispatch:Dispatch &lt;动作&gt;; }`是我最后一个有效的界面 (3认同)
  • 谢谢,那确实消除了错误,但现在出现错误:`Type '{ state: { isAuth: boolean; 用户:字符串;}; 调度:调度&lt;操作&gt;;}' 不可分配给类型 'IContextProps'。属性“调度”的类型是不兼容的。类型 'Dispatch&lt;Actions&gt;' 不能分配给类型 '({ type }: { type: string; }) =&gt; void'。参数“value”和“__0”的类型不兼容。类型 '{ 类型:字符串;}' 不可分配到类型 'Actions'。类型“{ type: string;”中缺少属性“value” }' 但在类型 'ILogout' 中需要 (2认同)
  • 这对我不起作用。我收到错误消息:`禁止在对象文字上进行类型断言,而应使用类型注释。 (2认同)

Tra*_*ace 15

当 Typescript 将我制服到它应该为我服务的地方时,我感到反胃。
在这种特殊情况下,我真的不关心类型,我这样解决:

以下工作正常:

const ctx = createContext<any>({});
Run Code Online (Sandbox Code Playgroud)

大多数不使用 TS 的示例根本不带任何参数,而且我认为添加超出我需要的代码没有任何价值。


Jer*_*rad 9

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

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

这是一篇很好的文章,解释了完全相同的解决方法(不在 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;
Run Code Online (Sandbox Code Playgroud)