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'/>