Search code examples
javascriptreactjsreduxreact-router

useSelector only fires correctly after re-render in Redux store, React router


i have a situation with two pages. The first one is the main page, the right one is rendered accordingly. The right component gets rendered depending on the redux state and basically works correctly. Short example without if statements:

 const renderRightComponent = () => {
    return <ComponentOnRightSide />;
  };

and

return (
    <div>
      <ComponentOnLeftSide />
      {renderRightComponent()}
    </div>
  );

When a button on the ComponentLeftStide gets clicked, it dispatches an action to the redux store. It get's stored correctly (according to Chrome's redux Dev tools). However, I somehow need to manage to re-render the right component after the dispatch action - I am a bit lost with how I possibly could do that - probably missing the correct keyword - anyone has an idea?

A little more clarification: I'm using a DatePicker from material UI in the left component.

 onChange={(newValue) => {
            setValue(newValue);
          }}

This is the value that get's dispatched to react-redux. In the right component I'm using useSelector:

const [date, setDate] = useState(
    useSelector((state) => state.date)
  );

This one I'm then calling in the return statement with the help of the following function:

 const getTitle = () => {
    if (date) {
      return moment(date).format("D MMM, YYYY");
    }

    return "";
  };

If i console.log date it does not change in the right component.


Solution

  • The code you've shared isn't accessing the Redux state correctly.

    const [date, setDate] = useState(
      useSelector((state) => state.date)
    );
    

    React state is only ever initialized once when mounting the component. I suspect the reason why the code above isn't throwing React hooks warnings is due to the fact that the useSelector function is still called each render cycle (so it's not breaking the being conditionally called rule) and the result of subsequent calls is ignored.

    To correctly cache a local copy you'd want to split this out and use an useEffect hook to update the cached date value.

    Example:

    const dateState = useSelector((state) => state.date);
    
    const [date, setDate] = useState(dateState);
    
    useEffect(() => {
      setDate(dateState);
    }, [dateState]);
    

    Keep in mind that duplicating state locally is often considered anti-pattern in React. Just consume the Redux state directly.

    const date = useSelector((state) => state.date);
    
    ...
    
    const getTitle = () => {
      return date ? moment(date).format("D MMM, YYYY") : "";
    };