61

I'm trying to figure out how to notify another component about a state change. Let's say I have 3 components - App.jsx,Header.jsx,and SidebarPush.jsx and all I'm simply trying to do is toggle a class with an onClick.

So the Header.jsx file will have 2 buttons when clicked will toggle the states to true or false. The other 2 components App.jsx and Header.jsx will need to know about these state changes so they can toggle a class whenever those states change.

App.jsx

import React from 'react';
import Header from 'Header';
import classNames from "classnames";
import SidebarPush from 'SidebarPush';
import PageWrapper from 'PageWrapper';

var MainWrapper = React.createClass({
    render: function() {
        return (
            <div className={classNames({ 'wrapper': false, 'SidebarPush-collapsed': !this.state.sidbarPushCollapsed })}>
                <Header/>
                <SidebarPush/>
                <PageWrapper>
                {this.props.children}
                </PageWrapper>
            </div>
        );
    }
});

module.exports = MainWrapper;

Header.jsx

import React from 'react';
import ReactDom from 'react-dom';


class Header extends React.Component {
    constructor() {
        super();
        this.state = {
            sidbarPushCollapsed: false,
            profileCollapsed: false
        };
        this.handleClick = this.handleClick.bind(this);
    }
    handleClick() {
        this.setState({
            sidbarPushCollapsed: !this.state.sidbarPushCollapsed,
            profileCollapsed: !this.state.profileCollapsed

        });
    }
    render() {
        return (
            <header id="header">
                <ul>
                    <li>
                        <button type="button" id="sidbarPush" onClick={this.handleClick} profile={this.state.profileCollapsed}>
                            <i className="fa fa-bars"></i>
                        </button>
                    </li>
                    <li>
                        <button type="button" id="profile" onClick={this.handleClick}>
                            <i className="icon-user"></i>
                        </button>
                    </li>
                </ul>
                <ul>
                    <li>
                        <button id="sidbarOverlay" onClick={this.handleClick}>
                            <i className="fa fa-indent"></i>
                        </button>
                    </li>
                </ul>
            </header>
        );
    }
};

module.exports = Header;

SidebarPush.jsx

import React from 'react';
import ReactDom from 'react-dom';
import classNames from "classnames";


class SidebarPush extends React.Component {
    render() {
        return (
            <aside className="sidebarPush">
                <div className={classNames({ 'sidebar-profile': true, 'hidden': !this.state.pagesCollapsed })}>
                        ....
                </div>

                <nav className="sidebarNav">
                        ....
                </nav>
            </aside>
        );
    }
}


export default SidebarPush;
agdev
  • 653
  • 1
  • 7
  • 5

1 Answers1

80

Move all of your state and your handleClick function from Header to your MainWrapper component.

Then pass values as props to all components that need to share this functionality.

class MainWrapper extends React.Component {
    constructor() {
        super();
        this.state = {
            sidbarPushCollapsed: false,
            profileCollapsed: false
        };
        this.handleClick = this.handleClick.bind(this);
    }
    handleClick() {
        this.setState({
            sidbarPushCollapsed: !this.state.sidbarPushCollapsed,
            profileCollapsed: !this.state.profileCollapsed

        });
    }
    render() {
        return (
           //...
           <Header 
               handleClick={this.handleClick} 
               sidbarPushCollapsed={this.state.sidbarPushCollapsed}
               profileCollapsed={this.state.profileCollapsed} />
        );

Then in your Header's render() method, you'd use this.props:

<button type="button" id="sidbarPush" onClick={this.props.handleClick} profile={this.props.profileCollapsed}>
mnsr
  • 12,337
  • 4
  • 53
  • 79
  • 1
    Thank you for your reply, this makes sense. Starting from the top and passing to the child components. You have the header passing the props for the handle click and the profile state, which is good. But how would I pass the other state (sidbarPushCollapsed) down? The click trigger for that one is in the Header component as well. – agdev Aug 01 '16 at 01:37
  • Same way you pass profileCollapsed. You add whatever you want as an attribute. The attributes are then accessible through `this.props` in your child component [See updated answer] – mnsr Aug 01 '16 at 01:38
  • Oh ok, didn't realize I could attach multiple states like that. Thank you for your help! – agdev Aug 01 '16 at 01:52
  • 3
    what does this line `this.handleClick = this.handleClick.bind(this);` do? – Omer Jun 25 '17 at 10:48
  • 4
    @Omer It binds that function to the `this` context of the outside class. This way you can do things like `this.setState()` inside of the different methods. In this example binding to this binds to `MainWrapper` which receives a `setState` method from the extension of `React.Component` – mrClean Jul 28 '17 at 20:44
  • Or you can simply modify your handleClick function to something like this `handleClick = () => { this.setState({ }); }` and remove the binding statement – Shubham Chopra Nov 16 '18 at 06:21