In the below example, Apps (parent) component and its child components loads well on the startup. But if I select the tab (call handleClick) which updating the state with new tab value not re-rendering the child components (tabs, content) with updated value and the child props are not updating with parent state. Please advise. PS: I have added multiple console log for debugging purpose.
import React, { Component } from 'react';
var tabData = [
{name: 'Tab 1', isActive: true},
{name: 'Tab 2', isActive: false},
{name: 'Tab 3', isActive: false}
];
export class Tabs extends React.Component {
constructor(props) {
super(props);
this.state = {
activeTab: props.activeTab
};
}
componentWillReceiveProps(nextProps) {
console.log("Tabs:componentWillReceiveProps $$$$$$", this);
this.setState({activeTab: nextProps.activeTab});
}
render() {
console.log("Tabs $$$$$$", this.props);
return (
<ul className="nav nav-tabs">
{
tabData.map(function (tab, index) {
// console.log("####", this.props.activeTab);
return (
<Tab key={index} data={tab} isActive={this.props.activeTab === tab}
handleClick={this.props.changeTab.bind(this,tab)}/>
);
}.bind(this))}
</ul>
)
;
}
}
export class Tab extends React.Component {
componentWillReceiveProps(nextProps) {
console.log("Tab:componentWillReceiveProps $$$$$$", nextProps);
}
render() {
console.log("Tab $$$$$$", this.props);
return (
<li onClick={this.props.handleClick} className={this.props.isActive ? "active" : null}>
<a href="#">{this.props.data.name}</a>
</li>
);
}
}
export class Content extends React.Component {
constructor(props) {
super(props);
this.state = {
activeTab: props.activeTab
};
}
componentWillReceiveProps(nextProps) {
console.log("Tabs:componentWillReceiveProps $$$$$$", nextProps);
}
render() {
console.log("Content $$$$$$", this.state.activeTab);
console.log("Content props $$$$$$", this.props.activeTab);
return (
<div>
{this.state.activeTab.name === 'Tab 1' ?
<section className="panel panel-success">
<h2 className="panel-heading">Content 1</h2>
<p className="panel-body">chart1</p>
<p className="panel-body">handsontable1</p>
</section>
: null}
{this.state.activeTab.name === 'Tab 2' ?
<section className="panel panel-warning">
<h2 className="panel-heading">Content 2</h2>
<p className="panel-body">chart2</p>
<p className="panel-body">handsontable2</p>
</section>
: null}
{this.state.activeTab.name === 'Tab 3' ?
<section className="panel panel-danger">
<h2 className="panel-heading">Content 3</h2>
<p className="panel-body">chart3</p>
<p className="panel-body">handsontable3</p>
</section>
: null}
</div>
);
}
}
export class App extends React.Component {
constructor(props) {
super(props);
//this.activeTabs = tabData[0];
this.state = {
activeTab: tabData[0]
}
}
handleClick(tab) {
debugger;
console.log("1handleClick*****", tab, this);
this.setState({activeTab: tab});
console.log("2handleClick*****", this);
}
render() {
console.log("App $$$$$$", this.state.activeTab);
return (
<div>
<Tabs activeTab={this.state.activeTab} changeTab={this.handleClick}/>
<Content activeTab={this.state.activeTab}/>
</div>
);
}
}
React.render(
<App />,
document.getElementById('app')
);
You forgot to bind
the handleClick
event in App component, Use this:
changeTab={this.handleClick.bind(this)}
The way you are using should throw the error, check the console:
Can't read property setState of undefined
Suggestion:
1. It's not a good idea to bind
the props
functions in child component events, use arrow function
to call them.
So instead of:
handleClick={this.props.changeTab.bind(this,tab)}
Use this:
handleClick={() => this.props.changeTab(tab)}
2. Your are using es6
, so you can avoid the .bind(this)
to maintain the proper context
inside map
body, use arrow function
, like this:
tabData.map( (tab, index) => {
return (
<Tab key={index} data={tab} isActive={this.props.activeTab === tab} handleClick={this.props.changeTab.bind(this,tab)}/>
);
})
Check the working example:
var tabData = [
{name: 'Tab 1', isActive: true},
{name: 'Tab 2', isActive: false},
{name: 'Tab 3', isActive: false}
];
class Tabs extends React.Component {
constructor(props) {
super(props);
this.state = {
activeTab: props.activeTab
};
}
componentWillReceiveProps(nextProps) {
this.setState({activeTab: nextProps.activeTab});
}
render() {
return (
<ul className="nav nav-tabs">
{
tabData.map(function (tab, index) {
return (
<Tab key={index} data={tab} isActive={this.props.activeTab === tab}
handleClick={this.props.changeTab.bind(this,tab)}/>
);
}.bind(this))}
</ul>
)
;
}
}
class Tab extends React.Component {
componentWillReceiveProps(nextProps) {
}
render() {
return (
<li onClick={this.props.handleClick} className={this.props.isActive ? "active" : null}>
<a href="#">{this.props.data.name}</a>
</li>
);
}
}
class Content extends React.Component {
constructor(props) {
super(props);
this.state = {
activeTab: props.activeTab
};
}
componentWillReceiveProps(nextProps) {
}
render() {
return (
<div>
{this.state.activeTab.name === 'Tab 1' ?
<section className="panel panel-success">
<h2 className="panel-heading">Content 1</h2>
<p className="panel-body">chart1</p>
<p className="panel-body">handsontable1</p>
</section>
: null}
{this.state.activeTab.name === 'Tab 2' ?
<section className="panel panel-warning">
<h2 className="panel-heading">Content 2</h2>
<p className="panel-body">chart2</p>
<p className="panel-body">handsontable2</p>
</section>
: null}
{this.state.activeTab.name === 'Tab 3' ?
<section className="panel panel-danger">
<h2 className="panel-heading">Content 3</h2>
<p className="panel-body">chart3</p>
<p className="panel-body">handsontable3</p>
</section>
: null}
</div>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
activeTab: tabData[0]
}
}
handleClick(tab) {
this.setState({activeTab: tab});
console.log("2handleClick*****", tab);
}
render() {
return (
<div>
<Tabs activeTab={this.state.activeTab} changeTab={this.handleClick.bind(this)}/>
<Content activeTab={this.state.activeTab}/>
</div>
);
}
}
ReactDOM.render(
<App />,
document.getElementById('app')
);
<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'/>
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments