functional component doesn't re-render on props change
Asked Answered
P

3

5

In the code below, whenever I get new props from the parent, the new props are logged correctly on the console, but the rendered HTML is never updated after the initial render:

export default function(props) {
  const [state, setState] = useState(props)
  
  // initially, props.something is defined
  // every time props changes (from the parent) props.something is redefined as expected and logged here
  console.log(props.something)
  
  // initially, props.something is rendered correctly
  // every time props.something changes (from the parent) the HTML never updates
  return (
    {state.something && <div>{state.something}</div>}
  )
} 

I already tried using useEffect() even though I don't see the point, but it it didn't fix anything.

Pitre answered 24/5, 2022 at 21:43 Comment(0)
F
9

State will not update just because props change, this is why you need useEffect.

export default function(props) {
  const [state, setState] = useState(props)

  useEffect(() => {
    setState(props.something)
  }, [props.something])
  
  // initially, props.something is defined
  // every time props changes (from the parent) props.something is redefined as expected and logged here
  console.log(props.something)
  
  // initially, props.something is rendered correctly
  // every time props.something changes (from the parent) the HTML never updates
  return (<div>{state.something}</div>)
} 

adding props.something to the array as the second argument to useEffect tells it to watch for changes to props.something and run the effect when a change was detected.

Update: In this specific example, there is no reason to copy props to state, just use the prop directly.

Forrester answered 24/5, 2022 at 21:47 Comment(5)
I thought that since the whole function (component) is called every time, then the state is also reset every time.Pitre
If the state was reset every time the function rendered, then you wouldn't need state, you could just use a local variable (or props.something) directly. The whole point of state is that the functional component can preserve state between renders.Forrester
That is true. I'm trying to wrap my head around React coming from Ember. Thanks for the clarification, it makes sense.Pitre
+1 but: Is it just me, or does it feel much cleaner instead providing data via a Context provider (e.g. useContext hook) so that you can avoid copying props to state?Intercalation
Context seems like overkill for this one thing. But, in this specific example, there is no reason to copy props to state, just use the prop directly. Updated.Forrester
A
1

In your example you copy props to state only once, when initial values set.

It's almost never a good idea to copy props to component state though. You can read about it react docs

Agreeable answered 24/5, 2022 at 21:48 Comment(0)
G
1

You might not need an effect to sync state from props, See Resetting all state when a prop changes. You can pass a key prop to the component so that whenever the key changes, React will recreate the DOM and reset the state of the component and all of its children

import { useState } from 'react';

const MyComponent = (props) => {
  const [state, setState] = useState(props);
  console.log(props.something);
  return state.something ? <div>{state.something}</div> : null;
};

function App() {
  const [count, setCount] = useState(0);
  return (
    <>
      <button onClick={() => setCount((count) => count + 1)}>increase</button>
      <MyComponent something={count} key={count} />
    </>
  );
}

export default App;

stackblitz

Gershon answered 21/8 at 10:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.