Использование useReducer для отправки действия из одного дочернего компонента и обновления состояния в другом дочернем компоненте

Вот CodeSandbox с приведенным ниже примером кода, и линтер выделяет некоторые из проблем: https://codesandbox.io/s/react-repl-bw2h1

Ниже приведен базовый пример того, что я пытаюсь сделать. В компоненте контейнера у меня есть контекст AppContext, который предоставляет состояние дочерним компонентам <ChildConsumer /> и <ChildDispatcher />.

Компонент <ChildConsumer /> получает это состояние с помощью useContext, и, похоже, он работает, как ожидалось.

Внутри <ChildDispatcher /> я пытаюсь отправить действие при нажатии кнопки. Для этого я создал редуктор reducer, который обрабатывает действие. Я также установил здесь useReducer, который принимает reducer и начальное store состояние.

Когда я нажимаю кнопку, ничего не происходит. Я ожидаю, что dispatch получит как state, снятый с useReducer, так и объект action, и передаст их редуктору. Редуктор должен увидеть, что действие типа BUTTON_CLICKED было получено, и должен вернуть новое состояние, содержащее старое состояние, а также дополнительный элемент 'goodbye'. Затем дочерний компонент <ChildConsumer /> должен выполнить повторную визуализацию с этим новым состоянием.

import React, { createContext, useContext, useReducer } from "react";
import ReactDOM from "react-dom";

const store = [""];
const AppContext = createContext(store);

const ChildDispatcher = () => {
  const reducer = (state, action) => {
    switch (action.type) {
      case "BUTTON_CLICKED":
        return [...state, "goodbye"];
      default:
        return state;
    }
  };

  const [state, dispatch] = useReducer(reducer, store);
  const handleClick = () =>
    dispatch(state, {
      type: "BUTTON_CLICKED"
    });
  return <button onClick={handleClick}>press me</button>;
};

const ChildConsumer = () => {
  const [consumer] = useContext(AppContext);
  return <div>{consumer}</div>;
};

const App = () => {
  return (
    <div>
      <h1>Using Context and useReducer</h1>
      <AppContext.Provider value={["hello"]}>
        <ChildConsumer />
        <ChildDispatcher />
      </AppContext.Provider>
    </div>
  );
};

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

person rpivovar    schedule 07.03.2020    source источник
comment
Как бы вы на самом деле реорганизовали это, чтобы поместить каждого дочернего элемента в отдельный файл?   -  person gloria00    schedule 11.07.2020


Ответы (2)


Я внес небольшую поправку в ваш код.

вы должны передать отправку, как показано ниже. Dispatch ожидает аргумент типа object.

const handleClick = () => dispatch({ type: "BUTTON_CLICKED" });

И тогда к этому состоянию можно было бы получить доступ вот так.

const ChildDispatcher = () => {
  const reducer = (state, action) => {
    switch (action.type) {
      case "BUTTON_CLICKED":
        //action.state      // like this
        return [...state, "goodbye"];
      default:
        return state;
    }
  };

  const [state, dispatch] = useReducer(reducer, store);
  const handleClick = () =>
    dispatch(state, {
      type: "BUTTON_CLICKED"
    });
  return <button onClick={handleClick}>press me</button>;
};

По умолчанию response передаст состояние диспетчеру. но если вы хотите передать некоторые данные, вы можете добавить их в объект и передать этот объект для отправки.

const handleClick = () => dispatch({ type: "BUTTON_CLICKED", state: state });

CodeSandBox:

 Изменить реакцию на ответ

person Sohail Ashraf    schedule 07.03.2020
comment
Спасибо. Я могу видеть, как это обновляется в компоненте <ChildDispatcher /> из журнала, который вы создали. Возможно ли, чтобы это состояние также было передано компоненту <ChildConsumer />? - person rpivovar; 07.03.2020
comment
Не знал, что неправильно вызвал функцию отправки. Спасибо за совет по этому поводу тоже - person rpivovar; 07.03.2020
comment
да, вы также можете получить состояние в ChildConsumer, так как он также потребляет тот же Context - person Sohail Ashraf; 07.03.2020

Немного проблем с этим:

  1. Состояние ChildDispatch доступно только для ChildDispatch и не влияет на компоненты верхнего уровня. Чтобы изменить значение контекста, вам необходимо предоставить отправку в этом компоненте и создать настраиваемый обработчик (или передать его в качестве реквизита) для использования в ChildDispatch.

  2. Не передавайте состояние при звонке на вашу рассылку. useReducer сделает это за вас. Просто отправьте действие.

Это означает, что для вашего потока данных должно быть одностороннее направление. Родительские компоненты контролируют общее состояние / способ управления состоянием, а дочерние компоненты используют это для визуализации / выполнения действий.

person Asher Gunsay    schedule 07.03.2020
comment
Спасибо. Я думаю, что я изучал настройку этой функции редуктора в родительском элементе, а затем каким-то образом передавал отправку в качестве опоры через контекст, но у меня возникли проблемы (а также не был уверен, был ли это анти-шаблон или что-то, чего я не должен был '' не делаю). Я посмотрю на это подробнее. - person rpivovar; 07.03.2020
comment
Причина, по которой я решил передать его через контекст, а не просто как опору, заключается в том, что в моем реальном проекте дочерние компоненты вложены немного вниз, а не непосредственно под родительским компонентом. - person rpivovar; 07.03.2020