React hooks: родительский компонент не обновляется при вызове dispatch в дочернем компоненте - PullRequest
2 голосов
/ 06 июля 2019

Я следовал руководству по передаче редуктора дочерним компонентам с помощью useContext, но при диспетчеризации от дочернего компонента он, похоже, не перерисовывал родительский компонент.

Само состояниеКажется, корректно обновляется при отправке из дочернего компонента (как сделано console.logging state.count), но родительский компонент не отражает изменения в состоянии.

Я попытался передать функцию диспетчеризациикак опора для ребенка, и это обновляло App.js, но я думал, что мой подход был чище, и вместо этого я мог сделать что-то не так.Я также поместил {state.count} в Child.js, и он обновился должным образом, но для этой ситуации он мне нужен в родительском компоненте.Вот код:

App.js

import React, { useReducer } from 'react';
import { StateProvider } from './reducer';
import Child from './Child'

function App() {
    const reducer = (state, action) => {
        switch (action.type) {
            case 'add':
                console.log(state.count+1)
                return {...state, count: state.count + 1}
            default:
                throw new Error();
        }
    }

    const initialState = {count: 1};
    const [state, dispatch] = useReducer(reducer, initialState)

    return (
        <StateProvider initialState={initialState} reducer={reducer}>
            <div className="App">
                {state.count}
                <Child></Child>
            </div>
        </StateProvider>
  );
}

export default App;

reducer.js

import React, {createContext, useContext, useReducer} from 'react';

export const StateContext = createContext(null);

export const StateProvider = ({reducer, initialState, children}) => (
    <StateContext.Provider value={useReducer(reducer, initialState)}>
        {children}
    </StateContext.Provider>
);

export const useStateValue = () => useContext(StateContext);

Child.js

import React from 'react';
import { useStateValue } from './reducer';

export default function Child(props) {
    const [state, dispatch] = useStateValue();

    return (
        <button onClick={() => dispatch({type: 'add'})}>Increment</button>
    )
}

1 Ответ

2 голосов
/ 06 июля 2019

Это потому, что вы воссоздаете reducer в StateProvider:

<StateContext.Provider value={useReducer(reducer, initialState)}>

Вместо этого просто передайте фактический редуктор dispatch вниз:

<StateProvider initialState={initialState} reducer={{dispatch}}> 

А затем используйте его:

const StateProvider = ({reducer, initialState, children}) => (
    <StateContext.Provider value={reducer}>
        {children}
    </StateContext.Provider>
);

Кстати, я думаю, вы могли бы просто объявить логическую функцию редуктора вне вашего компонента, чтобы она не объявлялась повторно при каждом рендеринге App без причины.

Вот пример:

// mimic imports
const {createContext, useContext, useReducer} = React;

const StateContext = createContext(null);

const StateProvider = ({reducer, initialState, children}) => (
    <StateContext.Provider value={reducer}>
        {children}
    </StateContext.Provider>
);

const useStateValue = () => useContext(StateContext);

function Child(props) {
  const {dispatch} = useStateValue(StateContext);

  return (
      <button onClick={() => dispatch({type: 'add'})}>Increment</button>
  )
}

const reducer = (state, action) => {
  switch (action.type) {
      case 'add':
          console.log(state.count+1)
          return {...state, count: state.count + 1}
      default:
          throw new Error();
  }
}

function App() {
    const initialState = {count: 1};
    const [state, dispatch] = useReducer(reducer, initialState)
    return (
        <StateProvider initialState={initialState} reducer={{dispatch}}>
            <div className="App">
                {state.count}
                <Child></Child>
            </div>
        </StateProvider>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.1/umd/react-dom.production.min.js"></script>
<div id="root"/>
...