Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Component not updating when I change the props that I pass to it in React

I have a functional component which has a child component. The child component displays some text which is passed onto it from the parent component via props. When I change the text in the parent component and pass it down, the child component still holds the old text.

Below is the an minimal reproducible example of the parent component, MainPage.

function MainPage(){
    let text = "This is the original text";
    setTimeout(function(){ text = "This is the new text" }, 3000);
    return(<DisplayText text={text} />);
}

Below is DisplayText.

function DisplayText(props){
    return(<p>{props.text}</p>)
}

How do I update the child component so that it displays 'This is the new text' instead of 'This is the original text' after 3 seconds?

Thanks in advance!

like image 792
Dark Programmer Avatar asked Oct 10 '19 13:10

Dark Programmer


2 Answers

A component only updates once either its state changes or its props change. A state is a variable or set of variables which is remembered when the component re-renders. All other variables will go back to their default value as soon as the component re-renders. You can see it as the component's memory.

So in your case changing your text variable won't update your parent's state, and thus won't re-render the component, which in return won't re-render and update the child's component.

If you want your parent component to update it's state (and update the child's props) you need to declare your text variable like this:

const [text, setText] = React.useState("This is the original text");

Text is your variable, it is now included within your component's state and will be remembered when the component re-renders. You can give this any name you want.

setText is a function which updates your text variable and also re-renders your component and it's children. You can give this any name you want.

"This is the original text" is your initial state, the initial value for your text variable.

To update your state you can do something like this:

setText("This is the new text");

So in your case it will look something like this:

function MainPage(){
    const [text, setText] = React.useState("This is the original text");

    React.useEffect(() => {
        const timeout = setTimeout(function(){
            setText("This is the new text")
        }, 3000);

        return clearTimeout(timeout)
    }, []);

    return(<DisplayText text={text} />);
}

useEffect is necessary to be able to define your setTimeout as soon as the component mounts. It can be use to execute some code as soon as a certain variable (defined between the [] brackets) updates. For example: If you wrote it like this:

React.useEffect(() => {
    // execute some code
}, [text])

It would execute some code as soon as your text variables changes. Leave the [] brackets empty to only trigger useEffect when the component mounts and unmounts.

Within the useEffect hook you declare your setTimeout, this sets your timer as soon as the component mounts in this case. The return method within your useEffect clears your timeout again as soon as the component unmounts. This will prevent your timer from running indefinitely after your component unmounts.

like image 138
Luze Avatar answered Sep 30 '22 08:09

Luze


In order for the variable to add a listener for the prop in a child component, it should be part of the state of the parent component, and the way you wrote your parent component is wrong, as you defined a static variable using let.

You have two choices, either follow the react hooks example posted by Luze (which is missing the useEffect function that will trigger the setTimeout after the component loads), or transform your parent component to a class, as follows:

import React from 'react';

class MainPage extends React.Component {
  state={
      text: "This is the original text",
  };

  componentDidMount(){
     setTimeout(()=> { this.setState({
        text: "This is the new text"
      }) 
     }, 3000);
  }

  render(){
    return(<DisplayText text={text} />);
  }

}
like image 39
Esso Avatar answered Sep 30 '22 09:09

Esso