Specifi c Вопросы об использованииReducer + Context - PullRequest
0 голосов
/ 26 апреля 2020

Specifi c Вопросы об использованииReducer + Context

Здравствуйте,

Я практикую различные способы передачи данных в React, однако есть некоторые вещи, которые все еще уклоняются от меня даже после часы чистящих примеров и документации. Рассмотрим следующие вопросы в контексте следующего кода (очень маленькое приложение для песочницы, с которым я учусь):


0) Из того, что я понимаю, мне нужен доступ к методу отправки, чтобы обновить GlobalContext через редуктор в context.tsx. Кроме того, я должен иметь возможность доступа к тому же методу диспетчеризации в любом компоненте через метод useContext. Я просто не знаю, как добраться до метода отправки. от компонента, который импортирует GlobalContext. Когда я пытаюсь (как видно из header.tsx), возникает ошибка, которая говорит: «Диспетчер свойств не существует для типа« IGlobalState »». IGlobalState - это тип самого объекта состояния (который находится в context.tsx), а не возвращаемый результат createContext, но эта ошибка, кажется, считает иначе? Не уверен, что здесь не так.


1) В context.tsx я заставил VSCode заставить typecast {state, dispatch} к любому, так как передавал их в виде {} в значение prop GlobalContext.Provider не работает по какой-то причине? Каждый учебник и пример онлайн для реакции / ts не имеют этого обходного пути. Почему не работает так, как кажется, так легко для всех? Использование любого типа побеждает цель машинописи ...


2) В loader.tsx я точно понимаю, как используется useContext; это позволяет мне получить доступ к свойствам глобального состояния, например, с помощью синтаксиса «globalContext.isLoading». Однако в примерах , таких как этот , кажется, что они просто извлекают метод отправки прямо из возвращаемого значения useContext, что для меня не имеет смысла. Не будет ли useContext просто возвращать текущее состояние в форме объекта? Вот специфика c, найденная в ссылке выше, о которой я говорю:

import { GlobalContext } from "Components/Context"; // Assume this points to context.tsx

const ExampleComponent = () => {
  const globalState = useContext(GlobalContext);
  const { dispatch } = globalState;

  dispatch({ type: '' }) // Empty example
};

Вы можете видеть, что в заголовке я то же самое. js, но это не сработало , Так как именно это работает и как это должно работать? Я изо всех сил пытался понять, как автор статьи смог получить доступ к функции отправки с этими строками кода. Даже с той же самой установкой это все еще выдает ошибку, которую я понимаю, но я не понимаю, почему я получаю это (см. Questino 0).

Из того, что я понимаю, у меня должен быть доступ к методу отправки так как я завернул все приложение в GlobalContext.Provider в root index.tsx. Я также предположительно передал в состоянии и отправил как объект в реквизиты значения в GlobalStateProvider, хотя это был хакерский способ, которого я не понимаю (см. Вопрос № 1 выше).

Я уверен, что ответ не далеко, но я был бы признателен любому, кто мог бы просветить меня! СПАСИБО за любую помощь, React + Typescript так здорово! Я до сих пор очень люблю опыт разработки / обучения. Вот код приложения:


index.tsx

import React from "react";
import * as ReactDOM from "react-dom";
import { GlobalStateProvider } from "Components/Context";
import { App } from "Components/App";

const Index = () => {
  return (
    <GlobalStateProvider>
      <App />
    </GlobalStateProvider>
  );
};

ReactDOM.render(<Index />, document.getElementById("root"));

app.tsx

import React from "react";
import { Loader } from "Components/Loader";
import { Header } from "Components/Header";
import "Assets/styles.scss";

export const App = () => {
    return(
        <div className="app">
            <Loader />
            <Header />
        </div>
    );
};

header.tsx

import React, { useContext } from "react";
import { globalActions, GlobalContext } from "Components/Context";
import * as API from "Components/API";
import $ from "jquery";

export const Header = () => {
  const globalState = useContext(GlobalContext);
  const { dispatch } = globalState; // <-- QUESTION_0
    const handleKeyPress = async (event: React.KeyboardEvent<HTMLDivElement>) => {
    const searchbarInput: JQuery<HTMLElement> = $("input[name='searchbarInput']");
    const header: JQuery<HTMLElement> = $(".header");
    if (
      searchbarInput.val() &&
      (header.is(":hover") || searchbarInput.is(":focus")) &&
      event.key === "Enter"
    ) {
      searchbarInput.blur();

      dispatch({ type: globalActions.load });

      // DO STUFF HERE...
    }
  };

  return(
    <div className="header">
      <div className="searchbar">
        <input className="input" name="searchbarInput" onKeyPress={handleKeyPress} placeholder="Search..." type="text" />
        <svg>
          ... // searchbar icon
        </svg>
      </div>
    </div>
  );
};

context.tsx

import React, { createContext, useEffect, useReducer } from "react";

interface IGlobalActions {
    type: string;
    payload?: Array<string> | object | string;
};

interface IGlobalState {
  isLoading: boolean;
};

const globalActions = {
    load: "START_LOADING",
};

const initialState: IGlobalState = {
  isLoading: false,
};

const GlobalContext = createContext<IGlobalState>(initialState);
const { Provider } = GlobalContext;

const reducer = (state: IGlobalState, action: IGlobalActions) => {
    switch(action.type) {
        case globalActions.load:
            return { isLoading: true };
        default:
            throw new Error("Invalid action type (StateProvider)");
    };
};

const GlobalStateProvider = ({ children }: any) => {
  const [state, dispatch] = useReducer(reducer, initialState);

    useEffect(() => {
        // Trigger loading animation
        if (state.isLoading) {
            setTimeout(() => {
                dispatch({ 
                    type: globalActions.load
                });
            }, 2000)
        }
    },
    [state.isLoading]);

  return (
        <Provider value={{ state, dispatch } as any}> // <-- QUESTION_1
            {children}
        </Provider>
    );
};

export { globalActions, GlobalContext, GlobalStateProvider };

loader.tsx

import React, { useContext }  from "react";
import * as classnames from "classnames";
import { GlobalContext } from "Components/Context";

export const Loader = () => {
  const globalContext = useContext(GlobalContext);
  const classNames = classnames({"loader": true}, {"is-loading": globalContext.isLoading});
  const loadingAnimation = "https://link/to/loading/animation";

  return (
    <div className={classNames} >
      <img className="image" src={loadingAnimation} alt="Loading..." />
    </div>
  );
};

1 Ответ

0 голосов
/ 27 апреля 2020

Для любых будущих гуглеров:

Ответ здесь заключался в том, чтобы конкретно указать тип возвращаемого значения оператора switch. Этот тип должен быть тем же самым, который определяет форму вашего состояния. Вот рабочий файл context.tsx для справки:

import React, { createContext, useContext, useEffect, useReducer } from "react";

type Action = {
    type: string;
    payload?: Array<string> | object | string;
};

type State = {
    isLoading: boolean,
};

type Dispatch = (action: Action) => void;
type GlobalProviderProps = {children: React.ReactNode};

const GlobalStateContext = createContext<State | undefined>(undefined)
const GlobalDispatchContext = createContext<Dispatch | undefined>(undefined);

const initialState: State = {
    isLoading: false,
};

const globalReducer = (state: State, action: Action) => {
    switch(action.type) {
        case "SHOW_LOADER":
            const newState: State = {
                ...state,
                isLoading: true,
            };
            return newState;
        default:
            throw new Error("Invalid action type (StateProvider)");
    };
};

const GlobalStateProvider = ({ children }: GlobalProviderProps) => {
    const [state, dispatch] = useReducer(globalReducer, initialState);

    useEffect(() => {
        if (state.isLoading) {
            console.log("state.isLoading = true");
            setTimeout(() => {
                dispatch({ 
                    type: "SHOW_LOADER"
                });
            }, 2000)
        }
    },
    [state.isLoading]);

  return (
        <GlobalStateContext.Provider value={state}>
            <GlobalDispatchContext.Provider value={dispatch}>
                {children}
            </GlobalDispatchContext.Provider>
        </GlobalStateContext.Provider>
    );
};

const useGlobalState = () => {
  const context = useContext(GlobalStateContext)
  if (context === undefined) {
    throw new Error('useGlobalState must be used within a GlobalStateProvider')
  }
  return context;
};

const useGlobalDispatch = () => {
  const context = useContext(GlobalDispatchContext)
  if (context === undefined) {
    throw new Error('useGlobalDispatch must be used within a GlobalStateProvider')
  }
  return context;
};

export { useGlobalState, useGlobalDispatch, GlobalStateProvider };
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...