React组件未在setState

时间:2017-06-30 16:26:18

标签: reactjs axios setstate

tldr; version:父类在setState调用后不重新渲染...

我有一个渲染多个组件的仪表板视图。加载数据的子组件工作得很好。但我有仪表板的另一部分,我计划使用相同的数据集加载同一组件的多个版本。因此,我不是对同一个数据集进行6次api调用,而是通过父级将数据拉入并将其发送给子级。此外,我正在使用路由参数来指示要查找的公司。

包含自己的api调用的组件运行良好,与路径参数一起正常运行。它是我在我遇到问题的父母中进行api调用的组件。父进行渲染,然后是子进程,然后api调用结束并设置状态,但不重新加载。因此,当路径参数发生变化时,组件会显示前一次点击的数据(基本上总是落在后面)。

我已经尝试更改哪些反应事件与之相关联,我已经使用了映射功能,确保"这个"是"这"我想要的(它是)......没有骰子。我知道它正确更新状态,如果我在forceUpdate()中显示正确的数据(但是我不能这样做,那么它会强制我将forceUpdate()添加到react更新方法中会使它不断重新加载。)

任何想法?这是一些精简/混淆的代码:

...
class ClientDash extends Component {
    constructor(props) {
        super(props);
        this.state = { chartOptions: {}, data1: {}, data2: {}, data3: {}, data4: {}, data5: {}, data6: {}, loaded: false };
    }

    loadData(clientID) {
        var currYear = moment().year();
        var chartData = {
            labels: [(currYear-4).toString(), (currYear-3).toString(), (currYear-2).toString(), (currYear-1).toString(), currYear.toString()],
            datasets: [{
                type: 'bar',
                label: 'Cat1',
                backgroundColor: this.props.colors[0].borderColor,
                borderColor: 'white',
                borderWidth: 3,
                data: [null, null, null, null, null]
            }, {
                type: 'bar',
                label: 'Cat2',
                backgroundColor: this.props.colors[1].borderColor,
                borderColor: 'white',
                borderWidth: 3,
                data: [null, null, null, null, null]
            }, {
                type: 'bar',
                label: 'Cat3',
                backgroundColor: this.props.colors[2].borderColor,
                borderColor: 'white',
                borderWidth: 3,
                data: [null, null, null, null, null]
            }, {
                type: 'bar',
                label: 'Cat4',
                backgroundColor: this.props.colors[3].borderColor,
                borderColor: 'white',
                borderWidth: 3,
                data: [null, null, null, null, null]
            }]
        };

        var chartOptions = {
            tooltips: {
                mode: 'index',
                intersect: true
            },
            responsive: true
        };


        axios(apiCall)
            .then(d => {
                var data = d.data;
                var data1 = _.clone(chartData),
                    data2 = _.clone(chartData),
                    data3 = _.clone(chartData),
                    data4 = _.clone(chartData),
                    data5 = _.clone(chartData),
                    data6 = _.clone(chartData);


                /* some data manipulation */

                console.log('loading data');
                console.log(this);
                this.setState({ chartOptions: chartOptions, data1: data1, data2: data2, data3: data3, data4: data4, data5: data5, data6: data6, loaded: true });
                // this.forceUpdate();
            }).then()
            .catch(function (error) {
                console.log(error);
            })
    }

    componentWillUpdate(nextProps) {
        this.initComp(nextProps.params.clientID);
    }

    shouldComponentUpdate(nextProps) {
        return nextProps.params.clientID != this.props.params.clientID;
    }

    componentWillMount() {
        this.initComp(this.props.params.clientID);
    }

    render() {
        console.log('parent rendering')
// removed all unnecessary code below
        return (
            <div className="wrapper wrapper-content animated fadeIn">
                <div className="row">
                    <div className="col-lg-4">
                        <div className="ibox">
                            <div className="ibox-content">
                                <MetricsColumnChart metricsData={this.state.data1} options={this.state.chartOptions} />
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        )
    }

}

/**
 * Map the state to props.
 */
const mapStateToProps = (state) => ({
  ...state
});

/**
 * Map the actions to props.
 */
const mapDispatchToProps = (dispatch) => {
    return bindActionCreators(Actions, dispatch)
};


/**
 * Connect the component to
 * the Redux store.
 */
export default connect(
  mapStateToProps,
  mapDispatchToProps
)(ClientDash)

儿童

import React, { Component } from 'react';
import {Bar} from 'react-chartjs-2';
var _ = require('lodash');

class MetricsColumnChart extends Component {
    render() {
        console.log('child rendering')
        return(
            <Bar data={this.props.metricsData} options={this.props.options} />
        )
    }
}

export default MetricsColumnChart

这是控制台的样子。正如你所看到的,没有重新渲染和&#34;这个&#34;这是正确的&#34;这个&#34;: enter image description here

1 个答案:

答案 0 :(得分:5)

问题在于你的shouldComponentUpdate。现在,只有当新clientID的道具与当前clientID不同时,它才会返回true。因此,当您在API调用结束时运行this.setState()时,组件不会更新,因为您没有收到新的道具

基本上,

  1. 接收初始道具
  2. componentWillMount运行
  3. 启动API调用(异步)
  4. componentWillMount完成
  5. 初始渲染
  6. 完成API调用
  7. 设置状态(this.setState)
  8. shouldComponentUpdate?没有新的道具,因此没有更新
  9. 接收新道具
  10. shouldComponentUpdate?是新的道具,因此是更新
  11. componentWillUpdate运行
  12. API调用启动(异步)
  13. componentWillUpdate完成
  14. 新渲染(使用当前可用状态)
  15. API调用已完成
  16. 设置新状态
  17. shouldComponentUpdate?没有新的道具,因此没有更新
  18. 有两种方法可以解决这个问题:

    1. 删除shouldComponentUpdate,以便React决定组件何时更新(运行this.setState或接收新更新时)
    2. 关注Tyler Sebastian's comment,向shouldComponentUpdate nextState添加第二个参数,并比较旧状态和新状态,以确定组件何时更新。
    3. 顺便说一句,我发现您在componentWillMount进行了API调用。 There are some good reasons why you should use componentDidMount isntead.