methis
methis

Reputation: 471

Reactjs parent component communicate with child of child component

i'm using ReactJS without FLux or Redux. I want the grand child component can communicate (read/update data) with his grandparents component.

Here's the parent component App:

export default class App extends React.Component {
 static propTypes = {
   children: React.PropTypes.object.isRequired
 };

  constructor(props) {
    super(props);
    this.state = {
      tabActive: 0,
    };
  }
  setTabActive(item) {
    this.setState({
      tabActive: item,
    });
  }
  render() {
    return (
      <div>
        <Header tabActive={this.state.tabActive} />
        <Content>
          {this.props.children}
        </ Content>
        <Footer />
      </div>
    );
  }
}

child component Header:

export default class Header extends React.Component {
  render() {
    return (
      <div>
          ....
         <SettingTabBar tabActive={this.props.tabActive} />
      </div>
    );
  }
}

Child of child component SettingTabBar:

export default class SettingTabBar extends React.Component {
  constructor(props) {
    super(props);
    this.state = { activeTab: this.props.tabActive  };
  }

  render() {
    if (location.pathname.indexOf('setting') > 0) {
      return (
        <Tabs activeTab={this.state.activeTab} onChange={tabId => this.setState({ activeTab: tabId })} ripple>
          <Tab>SETTING</Tab>
          <Tab>CHARTS</Tab>
          <Tab>HELP</Tab>
        </Tabs>
      );
    }
    return null;
  }
}

Are there anyway to make SettingTabBar component can update data to App/Header component via function setTabActive() when onChange?

Upvotes: 0

Views: 788

Answers (1)

Niyoko
Niyoko

Reputation: 7662

For communication with grandparent and grandchild, you can use context. It's not recomended, but it's working.

// root component
class App extends React.Component {
    constructor(props){
        super(props);
        this.state = {
            activeMenu: "none"
        };
    }

    getChildContext() {
        return {
            rootCallback: menuName => {
                this.setState({activeMenu: menuName});
            }
        }
    }

    render() {
        return (
          <div>
            <div>Current active menu is: <strong>{this.state.activeMenu}</strong></div>
            <Child />
          </div>
        );
    }
}

// declare childContextTypes at context provider
App.childContextTypes = {
    rootCallback: React.PropTypes.function
}

// intermediate child
class Child extends React.Component {
    render() {
        return (
            <div>
                <GrandChild />
            </div>
        );
    }
}

// grand child
class GrandChild extends React.Component {
    render() {
        return ( 
            <div>
                {/* get context by using this.context */}
                <button 
                    type="button" 
                    onClick={()=>this.context.rootCallback("one")}
                >
                    Activate menu one
                </button>
                <button 
                    type="button" 
                    onClick={()=>this.context.rootCallback("two")}
                >
                    Activate menu two
                </button>
                <button 
                    type="button" 
                    onClick={()=>this.context.rootCallback("three")}
                >
                    Activate menu three
                </button>
            </div>
        )
    }
}

// also declare contextTypes at context consumer
GrandChild.contextTypes = {
     rootCallback: React.PropTypes.function
}

// render it to DOM
ReactDOM.render(<App /> , document.getElementById('app-mount'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<div id="app-mount"></div>

Upvotes: 2

Related Questions