2017-06-30 141 views
0

tldr;版本:家長沒有重新渲染setState後...React組件不在setState上重新渲染

我有一個儀表板視圖,呈現多個組件。加載數據的子組件工作得很好。但我有儀表板的另一部分,我打算使用相同的數據集加載同一組件的多個版本。因此,我不是爲同一個數據集創建6個API調用,而是通過父項將數據提交給孩子。此外,我正在使用路由參數來指示要查找哪家公司。

包含自己的api調用的組件工作得很好,對路由參數運行正常。這是我在父母那裏進行api調用的組件,我有這個問題。父級正在渲染,然後是子級,然後api調用完成並設置狀態,但不是重新加載。因此,當路由參數改變時,組件顯示前一次點擊的數據(基本上總是落後一個)。

我試着改變哪些反應事件將其綁定到,我已經使用映射函數,確保「this」是我想要的「this」(它是)...沒有骰子。我知道它正確地更新狀態,如果我forceUpdate()組件中顯示正確的數據(但我不能這樣做,它會強制我將forceUpdate()添加到反應更新方法,這將導致它不斷重新加載)。

任何想法??下面是一些簡裝/混淆代碼:

家長

... 
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 

這裏的控制檯的樣子。正如你所看到的,沒有重新渲染和「this」是正確的「this」: enter image description here

+0

如果家長沒有更新,孩子也不會。你必須相應地更新你的'shouldComponentUpdate'(它接受第二個參數'nextState') –

+1

@TylerSebastian是對的。如果您使用控制檯登錄'shouldComponentUpdate',它應該到達那裏。它在那裏停了下來。 –

+0

感謝您的幫助,您是對的。我只是在'shouldComponentUpdate'方法的返回中添加了一個'!(_。isEqual(nextState,this.state))'' – Couch

回答

2

問題出在你的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?沒有新的道具,因此沒有更新

有兩種方法來解決這個問題:

  1. 刪除shouldComponentUpdate讓陣營決定何時組件更新(當您運行this.setState或接收新的更新)
  2. 按照Tyler Sebastian's comment,並將第二個參數添加到shouldComponentUpdatenextState,並比較舊狀態和新狀態以確定組件何時更新。

順便說一句,我看到你有你的API調用componentWillMountThere are some good reasons why you should use componentDidMount isntead.

相關問題