2

I'm learning react and while working on a bigger project I created this mockup to show the issue I'm having.

The parent component maintains a value in state which it passes to children via props. I want this value to propagate to children and update there when it is changed in the parent state. This works in the first version of this code:

import React from "react"
import Child from './Child'

export default class Parent extends React.Component {
    
    constructor() {
        super();
        this.state = {
            single_val: false,
        }
    }

    render() {

        return(
            <div className="Parent" style={{border: "solid orange 1px", padding: "15px"}}>
                <p>Parent val: {this.state.single_val.toString()}</p>
                <Child parent_val={this.state.single_val}/>
                <Child parent_val={this.state.single_val}/>
                <Child parent_val={this.state.single_val}/>
                <div className="switch" 
                    style={{height: "50px", width: "50px", backgroundColor: "lightPink"}}
                    onClick={(e)=>{this.setState({single_val: true})}}
                >
                </div>
            </div>
        )
    }
}

However, in the final version of the project, I need to create the children dynamically. I do it like this:

import React from "react"
import Child from './Child'

export default class Parent extends React.Component {
    
    constructor() {
        super();
        this.state = {
            single_val: false,
            children_divs: [],
        }
        this.setUp = this.setUp.bind(this);
    }

    componentDidMount() {
        this.setUp();
    }

    setUp() {

        var baseArray = [...Array(3)];
        var children = baseArray.map((elem)=>{
            return (<Child parent_val={this.state.single_val} />)
        });

        this.setState({children_divs: children});
    }

    render() {

        return(
            <div className="Parent" style={{border: "solid orange 1px", padding: "15px"}}>
                <p>Parent val: {this.state.single_val.toString()}</p>
                
                {this.state.children_divs}

                <div className="switch" 
                    style={{height: "50px", width: "50px", backgroundColor: "lightPink"}}
                    onClick={(e)=>{this.setState({single_val: true})}}
                >
                </div>
            </div>
        )
    }
}

...and the value no longer propagates to children when I press the button and change the parent's state: results screenshots.

How to keep the dynamic creation of child divs and still have the parent value propagate? I sense the issue might me because the value and children divs array are both maintained in the parent state but I'm not sure how to fix it. Hours of searching and looking at examples suggest I should recreate children divs from scratch - run the setUp again - but it seems like an overkill for one state value that I thought should propagate anyway.

Child component code for reference:

import React from "react"

export default function Child(props) {
    return (
        <div className="Child">
            <p>Child val: {props.parent_val.toString()}</p>
        </div>
    )
}

P.S. I even experimented with adding componentDidUpdate() to children to try and receive props again, but it never triggered.

Vidare17
  • 45
  • 1
  • 6

1 Answers1

0

Ok, so the problem here is your children_divs are created once and value of single_val is added/sent to them at that time (when you have created them in setUp function. Solution is simple, have your children created in render function, as render is called each time your state changes. This also removes your children_divs from state as its only used to render and serve no other purpose.

import React from "react"
import Child from './Child'

export default class Parent extends React.Component {
    
    constructor() {
        super();
        this.state = {
            single_val: false,
        }
    }

    render() {
        var baseArray = [...Array(3)];
        var children_divs= baseArray.map((elem)=>{
            return (<Child parent_val={this.state.single_val} />)
        });

        return(
            <div className="Parent" style={{border: "solid orange 1px", padding: "15px"}}>
                <p>Parent val: {this.state.single_val.toString()}</p>
                
                {children_divs}

                <div className="switch" 
                    style={{height: "50px", width: "50px", backgroundColor: "lightPink"}}
                    onClick={(e)=>{this.setState({single_val: true})}}
                >
                </div>
            </div>
        )
    }
}
Sagar More
  • 466
  • 2
  • 8
  • That works! And it does make me glad that children_divs doesn't clutter the state anymore... although it appears that setting up the entire children array from the start again is unavoidable, which is a bit disappointing. I thought the react's magic was that it would keep the divs unchanged and alter only the one little property that changed (the end goal children are much more complex than the example here). – Vidare17 Oct 06 '20 at 10:50
  • Don't worry, react will manage how the children's are to be updated on screen/DOM. It's just that, when you create children div's and store them in state, react will expect it to be managed by you. And since, you want them to be updated whenever state changes, do that in render. React will take care of DOM optimizations – Sagar More Oct 06 '20 at 10:54
  • @Vidare17 if this solutions works, can you mark this as accepted – Sagar More Oct 06 '20 at 10:55
  • Glad I could help! – Sagar More Oct 06 '20 at 11:08