skip to Main Content

Very new to React.js, I’ve been unable to trigger a re-render when a property passed to a child component changes.

I’ve made this simple example from following what I’ve seen in similar answers on Stack Overflow, but I seem to be missing something fundamental. Any help appreciated.

In my local test using routes, if I change page, then back to the page containing the child element, it does update, but not at the time of changing the property. Only when I force a re-render by navigating to another page / back to the page I want updated.

App.jsx:

import React from 'react';
import ChildTest from './ChildTest'

export function App(props) {

  const sets = { Setting1:"First" };

  setTimeout(() => {sets.Setting1 = "Second"; console.log("Timeout Called")}, 2000);
  setTimeout(() => { console.log(sets)}, 3000);

  return (
    <div className='App'>
        <ChildTest settings={sets} />
    </div>
  );
}

ChildTest.jsx

import Reac, {useEffect, useState } from 'react';

export default function ChildTest({settings}) {
  
  const [value, setValue] = useState(settings.Setting1);
  
  useEffect(() => { setValue(settings.Setting1) }, [settings.Setting1]);

  return (
    <div>Hello: {value} </div>
  );


}

Playcode example: https://playcode.io/1649072

I’ve tried most of the answers of similar issues found on Stack Overflow, for example here: React: why child component doesn't update when prop changes. This current example uses https://stackoverflow.com/a/54568167/19076076 which seems to be the most up to date answer.

2

Answers


  1. The only way to cause react to rerender is to set state. So you will need to turn sets into a state. Also, you shouldn’t set timeouts in the middle of rendering, as this can easily lead to unexpected extra timeouts or infinite loops. Instead, set the timeout in a useEffect so you can control when it happens. This will also let you add in cleanup logic to handle the component unmounting.

    export function App(props) {
      const [sets, setSets] = useState({ Setting1:"First" });
    
      useEffect((() => {
        const id = setTimeout(() => {
          setSets(prev => {
            return {
              ...prev,
              Setting1: "Second"
            }
          });
          console.log("Timeout Called")
        }, 2000);  
    
        // Return a cleanup function, in case the component unmounts
        return () => clearTimeout(id);  
      }, []);
    
    
      return (
        <div className='App'>
            <ChildTest settings={sets} />
        </div>
      );
    }
    

    P.S, in ChildTest you’re putting a prop in a state and then manually writing a useEffect to synchronize the prop with the state. This is usually a sign that you shouldn’t be creating a state in the first place. Instead, just use the prop directly:

    export default function ChildTest({settings}) {
      return (
        <div>Hello: {settings.Setting1} </div>
      );
    }
    
    Login or Signup to reply.
  2. I offer this just as a more in depth explanation for why the above answer works. It seems from your comment you might have missed the point.

    The core issue here is misunderstanding rendering and state in react. This answer should help fix that core issue.

    Loosely speaking, the following happens. First, the code in your parent file starts running. That means divs show up and functions get called. This includes the "return" with all the jsx divs and such. The code in the return portion (generally) encompasses what shows up on your screen.

    After the code is executed and things appear on your screen, the set timeout fires. This reassigns to the "sets" value, as you seem to expect. The problem is that nothing has told react it needs to do any dom updating. So you have a state change in the ‘background.’ But nothing has told react to update what the user sees.

    The setState hook does two things relevant to your confusion. First it actually stores the state like your "sets" object. But it also does something else. It also watches for updates to that state object. When it sees an update, it tells react to update. You can think of it like running the return() code again.

    Once react has been notified the dom needs to update, then the new values will be passed to the children and rendered.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search