Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Array of components doesn't update when props change

Codesandbox link

I have two components, a Parent and a Child. The Parent components keeps a list of Children in state and renders it using the map method.

The parent:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

import { useState } from "react";
import Child from "./Child";

export default function Parent(){
    const [counter, setCounter] = useState(0)
    const [childKey, setChildKey] = useState(0);
    const [children, setChildren] = useState([]);

    function addChild(){
        setChildren([...children, <Child counter={counter} index={childKey}/>]);
        setChildKey(childKey + 1);
    }

    return (
        <>
            <h2>The parent component</h2>
            <p>The counter is set to {counter}</p>
            <p><button onClick={() => {setCounter(counter + 1)}}>Increment</button></p>
            <p><button onClick={addChild}>Add child</button></p>

            {children.map((c, i) => {
                return (
                        <div key={i}>{c}</div>
                    );
            })}
            
        </>
    );
}

The Child:

export default function Child({counter, index}){
    return (
        <>
            <p>Child {index}: the counter is set to {counter}</p>
        </>
    );
}

Clicking the "add child" button adds an child to the children array.

Clicking the "increment" button updates the counter on the parent, but the counter in the children does not change.

Is there a way to re-render the children when the counter increments?
(while preferably keeping the children in an array)

Codesandbox link

>Solution :

setChildren([...children, <Child counter={counter} index={childKey}/>]);

Don’t put elements into state. It makes it really easy to have bugs like the one you’re having right now. You’ve effectively "locked in" what the props to the child are, so changes to the counter will not take effect.

Instead, just store the minimal data that’s needed to create the elements, and then create those elements during rendering. In your case, i think you just need a number saying how many children to render. Conveniently, you already have that in childKey, so i recommend:

export default function Parent(){
    const [counter, setCounter] = useState(0)
    const [childKey, setChildKey] = useState(0);

    const children = [];
    for (let i = 1; i <= childKey; i++) {
      children.push((
        <div key={i}>
          <Child counter={counter} index={i} />
        </div>
      ));
    }

    function addChild(){
        setChildKey(childKey + 1);
    }

    return (
        <>
            <h2>The parent component</h2>
            <p>The counter is set to {counter}</p>
            <p><button onClick={() => {setCounter(counter + 1)}}>Increment</button></p>
            <p><button onClick={addChild}>Add child</button></p>

            {children}
            
        </>
    );
}

Maybe childKey could be renamed to childCount.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading