0

I'm trying to refactor React.useReducer to redux (Redux Toolkit precisely). I have a parent -> child wrapper component. I'm getting an error as below while calling dispatch():

Cannot update a component (`Child`) while rendering a different component (`Parent`)

In both Parent and Child I use selector. The code in short looks like that:

export const Child: React.FC<Props> = () => {
  const state = useSelector((state: RootState) => state.slice);

  const doAction = () => {
    dispatch(update());
  }

  return (<div></div>)
}

export const Parent: React.FC<Props> = () => {
  const state = useSelector((state: RootState) => state.slice); // <-- problem

  doSomethingWithState(state);

  return (<div></div>)
}

The fix I found is to import store like that:

import { store } from 'redux/store';

export const Parent: React.FC<Props> = () => {
  const state = store.getState();

  doSomethingWithState(state);

  return (<div></div>)
}

I've read here that relying on the store being a singleton exported from some module is not recommended way. Is there a more proper way to get the actual state from redux and don't get the error as above?

Saletra
  • 1
  • 1
  • I feel you are omitting important information here. How do Parent and Child render each other, and what is `doSomethingWithState`? – phry Jul 05 '21 at 22:07
  • It's done as component names suggests: . What doSomethingWithState() does is not really matter as the error is thrown when I'm trying to get state by the selector. – Saletra Jul 05 '21 at 22:15
  • Well, it certainly seems like one of your `doSomethingWithState` is triggering a dispatch during render, at least that would be the easiest explaination for this error message. So, either you comment out all code that you have not shown here and test that it still throws the error, or you share that other code here. Otherwise nobody can help you. – phry Jul 06 '21 at 07:24
  • No. The problem is in useSelector() - it causes rerender. When I remove it, app works. – Saletra Jul 06 '21 at 08:06
  • Yes. useSelector causes a rerender whenever the store changes. My conclusion: If you change your store *during a render* it will cause a rerender during render. So there is a good chance that you do something during render that you should not do (like doing a dispatch). – phry Jul 06 '21 at 11:27
  • Thanks for confirming. I think another way is to pass down the state gathered from useSelector to the children. This also works. – Saletra Jul 06 '21 at 14:11
  • That is not the right solution. *You must not dispatch during render*. Using `useSelector` wherever is fine and only a symptom of a bug elsewhere. In React, you generally must not have any sideeffects during a render. React could rerender your component a hundred times in a second, triggering that side effect a hundred times. Sideeffects belong into useEffect with a correct dependency array. – phry Jul 06 '21 at 19:31

0 Answers0