1
votes

I have a component that is being called, then called again with updated information, but the var topics is not being updated by react. Can someone explain why the first example does not update on the screen, but the second one does? Does it have something to do with the useState call?

App.js calls DisplayQuestions once during initial render, then the page takes in user input and re-renders.

<DisplayQuestionsByTopic topics={topics} />
.....

topics var is NOT updated on screen:

export function DisplayQuestionsByTopic(props) {
    console.log(`DisplayQuestionsByTopic`, props.topics)
    const [topics, setTopics] = useState(props.topics)   //<<<<<<<<
    render(<h1>topics.join(',')</h1>)
}

topics var IS updated on screen:

export function DisplayQuestionsByTopic(props) {
    console.log(`DisplayQuestionsByTopic`, props.topics)
    render(<h1>props.topics.join(',')</h1>)          //<<<<<<<<<<<
}
1
Once you assign it to a state variable as in useState(props.topics), rerendering will not update it. You will have to unmount it completely and then remount it again for the new props.topics to be assigned. If it overrode the state var on every render, it would defeat the idea behind react state.codemonkey
@codemonkey That makes sense.... Is the second (working) example the "best practice" way to do this in React then?Rilcon42
It depends. If you want to take props.topics over and manage it inside a state in the child component, then no. If your requirements allow you to manage it from the parent and send it down as a static variable into the child on every render, then heck yeah. To answer the question of "best practice", one really needs to see the complete picture.codemonkey

1 Answers

0
votes

The argument passed to useState is only considered the first time the component renders - that is, on mount. On mount, the prop is put into the topics state. On further renders, changes to the prop don't result in a change to the state because the state has already been initialized to its initial value before.

While you could use useEffect to change the state in the child when the prop changes:

useEffect(() => {
  setTopics(props.topics);
}, [props.topics]);

Unless you're using setTopics somewhere, it would make more sense just to render the prop, like you're doing in the final snippet.

If you do need to do setTopics in the child, consider passing down the state setter from the parent instead - that way, all the state is handled in the parent, and you don't have duplicate state holders in different components that need to be synchronized.