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

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

Ниже приведена основа c пример того, что я пытаюсь сделать. В компоненте контейнера у меня есть контекст 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);

Ответы [ 2 ]

1 голос
/ 07 марта 2020

Мало проблем с этим:

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

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

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

1 голос
/ 07 марта 2020

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

вы должны передать отправку, как показано ниже. Диспетчер ожидает аргумент типа объекта.

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>;
};

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

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

CodeSandBox:

Edit react repl

...