Передача состояния с помощью useContext в Typescript - PullRequest
1 голос
/ 02 октября 2019

Я пытаюсь использовать хук useContext для передачи состояния и setState дочернему компоненту, но я получаю ошибку ts, когда пытаюсь передать [state, setState] в аргументе значения поставщика. Мой код выглядит следующим образом:


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 I'm.

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 ]

1 голос
/ 02 октября 2019

Для 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>
  );
};

Если вы хотите иметь чистый способ с собственным типом состояния и использовать тип React в useState, вы можете реализовать его следующим образом:

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

0 голосов
/ 02 октября 2019

Ниже приведен пример реализации React.Context с использованием TypeScript и hooks .

См. 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>

}
...