React:子组件不在父状态更改时呈现
React: Child component not rendering on parents state change
在下面的示例中,Apps(父)组件及其子组件在启动时加载良好。但是,如果我 select 使用新选项卡值更新状态的选项卡(调用 handleClick)不会使用更新的值重新渲染子组件(选项卡、内容),并且子道具不会使用父状态更新。
请指教。
PS: 我添加了多个控制台日志用于调试目的。
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')
);
您忘记了 bind
App 组件中的 handleClick
事件,使用这个:
changeTab={this.handleClick.bind(this)}
您使用的方式应该抛出错误,检查控制台:
Can't read property setState of undefined
建议:
1.子组件事件中的props
函数不宜bind
,使用arrow function
调用。
所以代替:
handleClick={this.props.changeTab.bind(this,tab)}
使用这个:
handleClick={() => this.props.changeTab(tab)}
2.你正在使用es6
,所以你可以避免.bind(this)
来维护[=24=里面的正确context
] 正文,使用 arrow function
,像这样:
tabData.map( (tab, index) => {
return (
<Tab key={index} data={tab} isActive={this.props.activeTab === tab} handleClick={this.props.changeTab.bind(this,tab)}/>
);
})
检查工作示例:
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'/>
在下面的示例中,Apps(父)组件及其子组件在启动时加载良好。但是,如果我 select 使用新选项卡值更新状态的选项卡(调用 handleClick)不会使用更新的值重新渲染子组件(选项卡、内容),并且子道具不会使用父状态更新。 请指教。 PS: 我添加了多个控制台日志用于调试目的。
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')
);
您忘记了 bind
App 组件中的 handleClick
事件,使用这个:
changeTab={this.handleClick.bind(this)}
您使用的方式应该抛出错误,检查控制台:
Can't read property setState of undefined
建议:
1.子组件事件中的props
函数不宜bind
,使用arrow function
调用。
所以代替:
handleClick={this.props.changeTab.bind(this,tab)}
使用这个:
handleClick={() => this.props.changeTab(tab)}
2.你正在使用es6
,所以你可以避免.bind(this)
来维护[=24=里面的正确context
] 正文,使用 arrow function
,像这样:
tabData.map( (tab, index) => {
return (
<Tab key={index} data={tab} isActive={this.props.activeTab === tab} handleClick={this.props.changeTab.bind(this,tab)}/>
);
})
检查工作示例:
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'/>