【问题标题】:React Typed ContextReact 类型化上下文
【发布时间】:2021-02-12 15:30:50
【问题描述】:

我正在尝试将 Context API 与 Typescript 一起使用,但没有任何工作正常,我很困惑,我已经在互联网上查阅了很多文章,但仍然无法让我理解这件事。

import { createContext, useState } from 'react';

export const SelectedContext = createContext<number>();

export const SelectedContextProvider = (props) => {
    const [selectedId, setId] = useState(null);
    const changeValue = (value:number | null) => {
        setId(value);
    }
    return (
        <SelectedContext.Provider value={{selectedId, changeValue}}>
            {props.children}
        </SelectedContext.Provider>
    )
}

这就是我制作上下文的方式。我希望它具有 id: null 作为默认值,但是当用户在 Select 我要设置的选项中选择一个选项时,它是数字并进行 API 调用。但是现在我必须为上下文指定默认值,如果我这样做了,那么 typescript 会显示一个错误,而不是 type null 不能分配给我希望它在 handleChange 函数中的数字。

我的第二个问题是,如果我的 props 是 react 子级,我该怎么说我的 props 将是什么类型,所以它可以是一个 React 节点数组的空数组,我不喜欢指定它因为在这种情况下使用打字稿没有用处。

        <SearchContextProvider>
            <CitySearchBar/>
            <CitySelect/>
        </SearchContextProvider>

会有这样的人为我解释一切吗?我是打字稿的新手,我不知道如何修复 anmything :(

所以我确实喜欢 vuongvu 的建议,现在我得到了

Type '{ selectedId: number | null; changeValue: (value: number | null) => 
void; }' is not assignable to type 'null'.ts(2322)
index.d.ts(335, 9): The expected type comes from property 'value' which is 
declared here on type 'IntrinsicAttributes & ProviderProps<null>'

上下文提供者的值属性

因此,在进行重大更改后,我只收到一个错误,但它仍然出现。似乎现在我的提供者只期望 setter 作为一个值,而不是 data 和 setter,我收到这样的错误:

Type '[MySelectedContextType, 
Dispatch<SetStateAction<MySelectedContextType>>]' is not assignable to type 
'Dispatch<SetStateAction<MySelectedContextType>>'.
 Type '[MySelectedContextType, 
 Dispatch<SetStateAction<MySelectedContextType>>]' provides no match for the 
 signature '(value: SetStateAction<MySelectedContextType>): void'.ts(2322)

所以我遇到的另一个问题是,当我尝试使用这个上下文时,我得到了这个错误:

TypeError: Object is not iterable (cannot read property 
Symbol(Symbol.iterator))

就像你说的那样,我明白了:

const [selectedId, setSelectedId] = useSelectedContext();

【问题讨论】:

    标签: reactjs typescript


    【解决方案1】:

    您遇到的主要问题是您的上下文不仅包含值,还包含设置器。因此,您的上下文不仅仅是一个数字,而是一个数字(或 null)和一个 setter,如下所示:

    type SelectedContextType = {
      selectedId: number | null,
      changeValue: (arg: number | null) => void
    }
    

    这就是我在 TypeScript 中通常使用上下文的方式:

    import React, { createContext, useState, useContext } from 'react';
    
    // I like the verbosity of creating a context type separate
    type SelectedContextType = {
      selectedId: number | null,
      changeValue: (arg: number | null) => void
    }
    
    // This is internal and should be set to SelectedContextType
    const SelectedContext = createContext<SelectedContextType | undefined>(undefined);
    
    // This is exported for other things to use and can be cast
    export const useSelectedContext = () => useContext(SelectedContext) as SelectedContextType
    
    
    export const SelectedContextProvider = (props) => {
        const [selectedId, setId] = useState<SelectedContextType['selectedId']>(null);
        const changeValue: SelectedContextType['changeValue'] = (value) => {
            setId(value);
        }
        return (
            <SelectedContext.Provider value={{selectedId, changeValue}}>
                {props.children}
            </SelectedContext.Provider>
        )
    }
    

    您也可以通过将 setter 直接传递给上下文值来稍微缩短它:

    export const SelectedContextProvider = (props) => {
        const [selectedId, setId] = useState<SelectedContextType['selectedId']>(null);
        return (
            <SelectedContext.Provider value={{selectedId, changeValue:setId}}>
                {props.children}
            </SelectedContext.Provider>
        )
    }
    

    本质上,您只是在“树上更高”useState,所以我建议摆脱所有样板并这样做:

    import React, { createContext, useState, useContext } from 'react';
    
    // this type is the actual type you are holding in state
    type MySelectedContextType = number | null;
    
    // this is what react returns when it calls useState with the type your are holding in state
    type SelectedContextType = [
      MySelectedContextType,
      React.Dispatch<React.SetStateAction<MySelectedContextType>>
    ];
    
    const SelectedContext = createContext<SelectedContextType | undefined>(undefined);
    
    export const useSelectedContext = () => useContext(SelectedContext) as SelectedContextType;
    
    // look how tiny this component is now? I love small components ?
    export const SelectedContextProvider: React.FC = ({children}) => (
        <SelectedContext.Provider value={useState<MySelectedContextType>(null)}>
            {children}
        </SelectedContext.Provider>
    );
    

    然后,你需要在哪里使用它

    export const SomeOtherComponent: React.FC = () => {
      // destructure it just like you would setState
      const [selectedId,setSelectedId] = useSelectedContext();
      return ( ... )
    }
    

    Working stackblitz

    【讨论】:

    • 该死,要理解的东西很多,明天我会试一试,但无论如何还是谢谢你,如果这解决了我的问题,我会很高兴。我还需要考虑几次才能知道我认为的一切:D
    • 如您所见,现在还有很多工作正在运行,但我仍然遇到了上面的值错误
    • @Konrad - 抱歉,我在SelectedContextType 上犯了一个错误,我会编辑。
    • 所以我不知道为什么,但我想它返回一个对象而不是数组,因为我现在遇到这种错误:/
    • 不好意思打扰你了,我忘了把provider放在我使用context inm的组件上,这就是这个错误发生的原因
    【解决方案2】:

    可以通过&lt;&gt;符号指定状态值类型,例如:

    const [selectedId, setId] = useState<number | null>(null);
    

    你的错误原因,如果你在初始化一个值时没有指定类型,打字稿会根据你输入的第一个值自动分配类型,所以在这种情况下:

        const [selectedId, setId] = useState(null);
    

    selectedId 将一直输入Null,因此您以后无法为其分配任何其他类型(例如您的情况下的数字)。

    【讨论】:

    • 我也试过这样,现在我在上下文提供者的 value 属性上遇到错误
    • 错误是什么?和原来的错误有关系吗?
    • 是的,它是同一个文件,我正在使用 useState 挂钩来存储上下文 bcs 它的功能组件的值
    • 上面的评论清楚地解释了一切,你应该可以解决它:)
    【解决方案3】:
    type Context = {
      selectedId: number | null;
      changeValue = (value:number | null) => void;
    }
    import { createContext, useState } from 'react';
    
    export const SelectedContext = createContext<Context>({} as Context);
    
    export const SelectedContextProvider:React.Fc = ({children}: {children: React.ReactNode}) => {
        const [selectedId, setId] = useState(null);
        const changeValue = (value:number | null) => {
            setId(value);
        }
        return (
            <SelectedContext.Provider value={{selectedId, changeValue}}>
                {children}
            </SelectedContext.Provider>
        )
    }
    export const useStore = () => React.useContex(SelectedContext);
    

    为了在整个应用程序中使用它:

    import {useStore} from './context';
    const {selectedId, changeValue} = useStore();
    

    【讨论】:

      猜你喜欢
      • 2018-03-13
      • 2017-03-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-17
      • 2018-12-22
      • 2015-09-30
      • 1970-01-01
      相关资源
      最近更新 更多