React组件不在setState上重新呈现

Cou*_*uch 2 setstate reactjs axios

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

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

包含自己的api调用的组件工作得很好,与路径参数一起正常运行.这是我在父母那里进行api调用的组件,我遇到了问题.父级正在渲染,然后是子级,然后api调用完成并设置状态,但不重新加载.因此,当路径参数发生变化时,组件会显示前一次点击的数据(基本上总是落后一个).

我已经尝试更改哪些反应事件将它绑定到,我使用映射函数,确保"this"是我想要的"this"(它是)...没有骰子.我知道它正在正确地更新状态,如果我在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)
Run Code Online (Sandbox Code Playgroud)

儿童

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
Run Code Online (Sandbox Code Playgroud)

这是控制台的样子.如您所见,没有重新渲染,"this"是正确的"this": 在此输入图像描述

Sam*_* I. 5

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

基本上,

  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?没有新的道具,因此没有更新

有两种方法可以解决这个问题:

  1. 删除,shouldComponentUpdate以便React决定组件何时更新(当您运行this.setState或接收新更新时)
  2. 按照泰勒塞巴斯蒂安的评论,并添加第二个参数shouldComponentUpdatenextState,和比较新旧状态,以确定何时该组件将更新.

顺便说一句,我看到你有API调用componentWillMount.你应该使用componentDidMountisntead 有一些很好的理由.