在TypeScript中使用useContext传递状态

时间:2019-10-01 23:17:07

标签: reactjs typescript react-hooks

我正在尝试使用useContext挂钩将状态和setState传递给子组件,但是当我尝试在提供程序的value参数中传递[state,setState]时遇到了ts错误。我的代码如下:


export interface IProviderProps {
  children?: any;
}

const initialState = {
  state: Object,
  setState: () => {},
};

export const AppContext = createContext(initialState);

export const AppProvider = (props: IProviderProps) => {
  const [state, setState] = useState([{ isMenuOpen: false, isSideOpen: false }]);

  return <AppContext.Provider value={[state, setState]}>{props.children}</AppContext.Provider>;
};

我在设置的initialState的值变量上遇到错误。

index.d.ts(290, 9): The expected type comes from property 'value' which is declared here on type 'IntrinsicAttributes & ProviderProps<{ state: ObjectConstructor; setState: () => void; }>'

如何设置初始状态以允许我传递状态和useState变量?

2 个答案:

答案 0 :(得分:2)

对于initialState,您想定义与AppProvider中使用的结构/形状相同,因为TypeScript从给定的初始值推断出整个上下文类型。因此,初始值的类型决定了消费组件能够使用的上下文形状。

在您的示例中,AppProvider上下文值与initialState不兼容。同样,initialState类型太“宽”而不能用于使用组件。例如。 Object的意思是,您希望Object构造函数为state(不是您真正想要的)。使用setState: () => {},使用上下文的组件将无法使用状态参数来调用此函数,因为该函数类型伪装成无参数的。

我在这里假设,一般状态应如下所示(并省略您在AppProvider中使用的其他数组包装器):

{ isMenuOpen: boolean; isSideOpen: boolean }

那么具有适当类型的初始状态将是:

const initialState = {
  // your state defaults
  state: { isMenuOpen: false, isSideOpen: false }, 
  // noop
  setState: (state: { isMenuOpen: boolean; isSideOpen: boolean }) => {}
};

AppProvider

export const AppProvider = (props: IProviderProps) => {
  const [state, setState] = useState({ isMenuOpen: false, isSideOpen: false });

  return (
    <AppContext.Provider value={{ state, setState }}>
      {props.children}
    </AppContext.Provider>
  );
};

如果您想以自己的状态类型和在useState中使用过的React类型来获得一种简洁的方式,则可以这样实现:

import { createContext, useState, Dispatch, SetStateAction } from "react";

type MyState = {
  isMenuOpen: boolean;
  isSideOpen: boolean;
};

type ContextValue = {
  state: MyState;
  // the type when you hover over setState in AppProvider
  setState: Dispatch<SetStateAction<MyState>>;
};

const initialState: ContextValue = {
  // your state defaults
  state: { isMenuOpen: false, isSideOpen: false },
  // noop
  setState: (state: { isMenuOpen: boolean; isSideOpen: boolean }) => {}
};

Sample

答案 1 :(得分:0)

下面是使用TypeScripthooksReact.Context的示例实现。

有关更多信息,请参见React - Context

// Dependencies.
import React, {createContext, useState, SetStateAction, Dispatch} from 'react'

// App Context.
interface AppContext {

  // Data.
  menuIsOpen: boolean
  sideIsOpen: boolean

  // Functions.
  setMenuIsOpen: Dispatch<SetStateAction<boolean>>
  setSideIsOpen: Dispatch<SetStateAction<boolean>>

}

// Context.
export const Context = createContext<AppContext>({

  // Data.
  menuIsOpen: false,
  sideIsOpen: false,

  // Functions.
  setMenuIsOpen: open => {},
  setSideIsOpen: open => {}

})

// Provider
export const Provider: React.FC<{}> = props => {

  // State.
  const [menuIsOpen, setMenuIsOpen] = useState(false)
  const [sideIsOpen, setSideIsOpen] = useState(false)

  // Data.
  const data = {menuIsOpen, sideIsOpen}

  // Functions.
  const functions = {setMenuIsOpen, setSideIsOpen}

  // Value.
  const value = {...data, ...functions}

  // ..
  return <Context.Provider value={value}>{props.children}</Context.Provider>

}