TransWikia.com

React: useState value doesn't update on parameter value change

Stack Overflow Asked by darksoulsong on January 5, 2022

In the example below, the value of the display state on Child component never updates, even if the show parameter toggles between true and false.

I expect it to receive the value and to update accordingly. Can someone please elaborate on why this is not working?

(I know I can use a useEffect callback and setDisplay(show) from inside it, but I’d like to know why a simpler approach like this doesn’t work)

function Child({ show }) {
  const [display] = React.useState(show);

  console.log({ show, display });
  return display ? "Message!" : null;
}

function Parent() {
  const [show, setShow] = React.useState(false);

  const handleClick = () => {
    setShow(!show);
  };

  return (
    <div>
      <div>
        <button onClick={handleClick}>Toggle</button>
      </div>
      <Child show={show} />
    </div>
  );
}

Working example: https://codesandbox.io/s/react-boilerplate-4hexp?file=/src/index.js

3 Answers

display is local component state of Child, given an initial value from props.show when Child mounted. There is never a state update within Child to render any other value of display. This is actually an anti-pattern to store passed props in local component state, but there are two alternatives/solutions to getting display to update.

Use an effect to update state when the props update

function Child({ show }) {
  const [display, setDisplay] = React.useState(show);
  useEffect(() => setDisplay(show), [show]);

  console.log(show, display);
  return display ? "Message!" : null;
}

Or better, just consume the prop show directly

function Child({ show }) {
  console.log(show);
  return show ? "Message!" : null;
}

The benefit of the latter is that the new value of show and the updated/rerendered UI occur in the same render cycle. With the former (the anti-pattern) the state needs to update then the component rerenders, so the updated UI is a render cycle delayed.

Answered by Drew Reese on January 5, 2022

I believe it's because the useState in the Child component is reading show when it first loads but then never updates because it's just set, it doesn't automatically update.

You could either just use show directly which should be used for return show ? 'message' : <></>

Or you could still use the local state with useState, but you would need to add a useEffect to listen to the props change then change the local state of that child.

Update:

Third option for your current code to work would also be to do:

{show && <Child show={show} />}

That way at the time when it's true, the component will read the latest data.

Answered by codingwithmanny on January 5, 2022

Well the value of display is set only on the first render of the component (because it is state and state doesnt change with renders, but only when you tell it to change). If you want it to be changing with changing props just use a normal constant instead.

Answered by Martin Červenka on January 5, 2022

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP