Реагировать на проблему createContext в Typescript?

Итак, у меня очень странная проблема с 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>
   );
}

Сообщение об ошибке:

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 возвращает ошибку: состояние не существует при нулевом значении. И то же самое, если я установил defaultState для контекста на {}.

И, конечно, если я

React.createContext();  

Затем TS кричит о том, что значение по умолчанию не предоставлено.

В песочнице все 3 варианта создания объекта контекста работают нормально.

Заранее благодарю за любой совет.

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
43
0
54 156
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Похоже, значение defaultValue для React.createContext должно иметь тип:

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<Actions>; }' is not assignable to type 'IContextProps'. Types of property 'dispatch' are incompatible. Type 'Dispatch<Actions>' is not assignable to type '({ type }: { type: string; }) => 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'

bauervision 13.02.2019 16:54

Вот мой последний: codeandbox.io/s/7jk69315l0 Теперь он фактически компилируется, но вылетает в браузере на: export function AdminStoreProvider(props: any) { с сообщением «TypeError: Object(...) is not a function»

bauervision 14.02.2019 20:34

Примечание: как только я импортировал Dispatch из React, а затем экспортировал и импортировал Actions, моя основная проблема была решена с предупреждениями и ошибками. interface IContextProps { state: IState; dispatch: Dispatch<Actions>; } был моим последним интерфейсом, который работал

bauervision 14.02.2019 20:38

Это не работает для меня. Я получаю сообщение об ошибке: Type assertion on object literals is forbidden, use a type annotation instead.

Jake 01.05.2019 16:26

@bauervision Является ли ответ полным, или кому-то нужно будет добавить код из вашего комментария (14 февраля в 19:38), чтобы он заработал? Если да, не могли бы вы обновить ответ соответствующим образом, чтобы принятый ответ содержал правильный код?

Remi 15.07.2019 10:24

Я весело провел время с этим, поэтому я решил поделиться тем, что придумал.

SidebarProps представляет состояние контекста. Все остальное, кроме действий редюсера, можно использовать как есть.

Вот хорошая статья, объясняющая точно такой же обходной путь (не в TypeScript): Смешивание хуков и контекстного 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;

Другие вопросы по теме