简体   繁体   中英

Reactjs: how to update state of parent from child

I am trying to update state of my parent component through child component via setState . below is my parent component:

Fulllayout

import React from 'react';
import { Route, Switch, Redirect } from 'react-router-dom';
import Header from '../components/header/header.jsx';
import Customizer from '../components/customizer/customizer';
import { Navbar, NavbarBrand, Collapse } from 'reactstrap';
export const settings = {
    navbarbg: 'skin1', 
    sidebarbg: 'skin6', 
    logobg: 'skin6' 
}
class Fulllayout extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            settings: settings
        };
    }
    render() {
        return (
             <div id="main-wrapper">
                <header data-navbarbg={this.state.settings.navbarbg}>
                    <Navbar expand="md" className={}></Navbar>
                </header>
                <div className="page-wrapper d-block"></div>
                <Customizer />
             </div>
        );
    }
}
export default Fulllayout;

in parent i have defined one constant settings which is exported. It is also given in the this.state . and in header, there is an attribute data-navbarbg={this.state.settings.navbarbg} .

I wanted to change its value dynamically. So, i have one customizer which is imported in parent as a child. Below is the child component:

customizer

import React from 'react';
import { settings } from '../../layouts/fulllayout';
import update from 'immutability-helper';

class Customizer extends React.Component {
    constructor(props) {
        super(props);
        this.navbarbgChange = this.navbarbgChange.bind(this);
        this.state = {
            settings: settings
        };
    }
    navbarbgChange(e) {
        var skin = e.currentTarget.dataset.navbarbg;
        var abc = update(this.state.settings, {
            navbarbg: { $set: skin }
        });
        this.setState({ settings: abc });
    }

    render() {
        return (
            <aside className="customizer" id="customizer">
                <a className="service-panel-toggle text-white"></a>
                <div className="customizer-body pt-3">
                    <div className="mt-3 border-bottom px-3">
                        <ul className="theme-color mb-2">
                            <li><a data-navbarbg="skin1" onClick={this.navbarbgChange}></a></li>    
                        </ul>
                    </div>
                </div>
            </aside>
        );
    }
}
export default Customizer;

From customizer, by clicking on color, i wanted to setState of parent to the value given in data-navbarbg attribute.

If i put this code in parent jsx file, it is working fine but for some reasons, this files should be kept separated.

So, what is missing in my code? or the whole approach is wrong? Thanks.

Is there a reason for navbarbgChange to be defined in Customizer ?

You could consider moving navbarbgChange to Fulllayout instead.

That way you can do

<li><a data-navbarbg="skin1" onClick={this.props.navbarbgChange}></a></li>

This will ensure that the Fulllayout has the updated background in its state. This also ensures that there is good separation of concerns since settings is defined in the parent and not the child component

In react you can always pass methods from parent to child. Let's write method in the parent to change the state of the parent from the child like this.

class Fulllayout extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            settings: settings
        };
    }

    // This will be passed to the child component

    changeForCustomizerState() {
        this.setState({
            settings: abc
        });
    }

    changeForHeaderState() {
        this.setState({
            settings: abc
        });
    }

    render() {
        return (
             <div id="main-wrapper">
                <header chageNavbarbg={this.changeForHeaderState}>
                    <Navbar expand="md" className={}></Navbar>
                </header>
                <div className="page-wrapper d-block"></div>
                <Customizer chageNavbarbg={this.changeForCustomizerState} />
             </div>
        );
    }
}

Then onClick on the child just call the parent method from the child which is passed from the parent.

render() {
        return (
            <aside className="customizer" id="customizer">
                <a className="service-panel-toggle text-white"></a>
                <div className="customizer-body pt-3">
                    <div className="mt-3 border-bottom px-3">
                        <ul className="theme-color mb-2">
                            <li><a data-navbarbg="skin1" onClick={this.props.chageNavbarbg}></a></li>    
                        </ul>
                    </div>
                </div>
            </aside>
        );
    }

The state update can be done in Parent from Child using callbacks. Check below code for better understanding

Fulllayout:

 updateState = (skin) => {
     this.setState({
          settings.navbarbg: skin
     });
}

render(){
   return(
      <div>
         <Customizer updateState={this.updateState}
      </div>
   );
}

And in your customizer

  navbarbgChange(e){
    const skin = e.currentTarget.dataset.navbarbg;
    this.props.updateState(skin);
}

OR

Fulllayout:

 updateState = (e) => {
     const skin = e.currentTarget.dataset.navbarbg;
     this.setState({
          settings.navbarbg: skin
     });
}

render(){
   return(
      <div>
         <Customizer updateState={this.updateState}
      </div>
   );
}

Customizer: directly pass parent function to onClick

  <li><a data-navbarbg="skin1" onClick={this.props.updateState}></a></li>

Also stop using var, and start using let and const mostly. ECMASCRIPT itself argues to avoid using var because of its window scope.

You can call parent's navbarbgChange(e) from child.

in parent:

navbarbgChange(e) {
    var skin = e.currentTarget.dataset.navbarbg;
    var abc = update(this.state.settings, {
        navbarbg: { $set: skin }
    });
    this.setState({ settings: abc });
}

<Child parentNavbarbgChange={navbarbgChange} />

in child call in navbarbgChange :

this.props.parentNavbarbgChange(e);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM