在componentDidUpdate中比较PrevProps

16

我正在尝试在已挂载的组件中的componentDidUpdate内检测属性是否更改。我有一个测试(下面代码中的refreshData)表现良好。是否可以以某种方式传递props,使它们不被componentDidUpdate(prevProps)检测到?

在component.js中:

componentDidUpdate(prevProps){

    //works fine
    if ( this.props.refreshData !== prevProps.refreshData ) {
        if ( this.props.refreshData )
            this.refreshData();
    }

    //these two arent calling
    if ( this.props.selectedCountries !== prevProps.selectedCountries ) {
        if ( this.props.selectedCountries )
            console.log('updated selected countries');
    }

    if ( this.props.selectedLocations !== prevProps.selectedLocations ) {
        console.log('updated selected locations');
    }

}

在 App.js 中传递 props 的方式如下:

selectLocation = (id, type, lng, lat, polydata, name, clear = false) => {

  //console.log(type);
  //console.log(lng);
  //console.log(lat);
  //console.log(polydata);

  let selectedType = 'selected' + type;
  let previousState = [];

  if (clear) {
    this.setState({
      selectedCountries: [],
      selectedLocations: [],
      selectedServices: [],
      selectedPoints: [],
      mapCenter: [lng, lat],
      locationGeoCoords: [polydata]
    })
    previousState.push(id);

  } else {

    previousState = this.state[selectedType];
    if (previousState.indexOf(id) === -1) {
      //push id
      previousState.push(id);

    } else {
      //remove id
      var index = previousState.indexOf(id)
      previousState.splice(index, 1);
    }
  }

  if (type === "Countries") {

    this.setState({
      selectedCountries: previousState,
      refreshData: true,
    })
  } else if (type === "Locations") {
    this.setState({
      selectedLocations: previousState,
      refreshData: true
    })
  } else if (type === "Points") {
    this.setState({
      selectedPoints: previousState,
      refreshData: true
    })
  }


}


render() {
  return (

    <component
      selectedCountries={this.state.selectedCountries}
      selectedLocations={this.state.selectedLocations}
      refreshData={this.state.refreshData} />
  }
}

1
看起来你没有正确地更新数组 - 如果你将 previousState 赋值给 this.state[whatever] 然后改变它,你实际上也会改变原始数组。你需要做的是像这样:previousState = [].concat(this.state[selected type]) - Rose Robertson
抱歉,我现在在手机上,无法写出一个恰当的答案和解释。希望这对你有所帮助! - Rose Robertson
4个回答

19

你好 :) 如我在评论中所述,问题出在你的App.js文件中 - 你正在改变一个数组。换句话说,当你认为你在创建一个新的选定国家的数组来传递时,你实际上正在更新原始数组,因此当你进行比较时,你总是在比较这两个完全相同的数组。

尝试像这样更新您的App.js -

selectLocation = (id, type, lng, lat, polydata, name, clear = false) => {

  //console.log(type);
  //console.log(lng);
  //console.log(lat);
  //console.log(polydata);

  let selectedType = 'selected' + type;
  let previousState = [];

  if (clear) {
    this.setState({
      selectedCountries: [],
      selectedLocations: [],
      selectedServices: [],
      selectedPoints: [],
      mapCenter: [lng, lat],
      locationGeoCoords: [polydata]
    })
    previousState.push(id);

  } else {

    previousState = [].concat(this.state[selectedType]);
    if (previousState.indexOf(id) === -1) {
      //push id
      previousState.push(id);

    } else {
      //remove id
      var index = previousState.indexOf(id)
      previousState.splice(index, 1);
    }
  }

  if (type === "Countries") {

    this.setState({
      selectedCountries: previousState,
      refreshData: true,
    })
  } else if (type === "Locations") {
    this.setState({
      selectedLocations: previousState,
      refreshData: true
    })
  } else if (type === "Points") {
    this.setState({
      selectedPoints: previousState,
      refreshData: true
    })
  }


}


render() {
  return (

    <component
      selectedCountries={this.state.selectedCountries}
      selectedLocations={this.state.selectedLocations}
      refreshData={this.state.refreshData} />
  }
}

唯一的区别在于设置previousState的那一行 - 我更新为:previousState = [].concat(this.state[selectedType]);。通过添加[].concat,我实际上每次都创建一个新的数组,因此当您通过push/splice对数组进行更改时,您将只修改新数组。然后,一旦将其作为props传递下去,比较就会正常工作 :) 有关阅读兴趣,我找到了一篇讨论这个问题的文章:https://medium.com/pro-react/a-brief-talk-about-immutability-and-react-s-helpers-70919ab8ae7c

没问题 :) 说实话,我相信@sushanth也会给你类似的建议,只是我有一个不公平的优势,你稍后更新了你的问题,加上了app.js。 - Rose Robertson
[].concat(this.state[selectedType]) 对我很有帮助!非常感谢。 - Jeffrey

4

selectedCountriesselectedLocations是数组对象。它们的引用永远不会改变。相反,应该检查它们的长度。

componentDidUpdate(prevProps){

    if ( this.props.refreshData !== prevProps.refreshData ) {
        if ( this.props.refreshData )
            this.refreshData();
    }

    if ( this.props.selectedCountries.length > prevProps.selectedCountries.length ) {
        if ( this.props.selectedCountries )
            console.log('updated selected countries');
    }

    if ( this.props.selectedLocations.length > prevProps.selectedLocations.length ) {
        console.log('updated selected locations');
    }
}

在上面的代码片段中,你似乎直接修改了this.state。状态应该是不可变的。始终确保使用concat添加元素,使用filter删除元素,因为它们会创建新的数组,而不是在状态中改变原始数组。按照以下方式做一些事情。
另外,大写组件名称是一个好习惯。
selectLocation = (id, type, lng, lat, polydata, name, clear = false) => {
        //console.log(type);
        //console.log(lng);
        //console.log(lat);
        //console.log(polydata);

        let selectedType = "selected" + type;
        let previousState = [];
        let updatedData = [];

        if (clear) {
          this.setState({
            selectedCountries: [],
            selectedLocations: [],
            selectedServices: [],
            selectedPoints: [],
            mapCenter: [lng, lat],
            locationGeoCoords: [polydata]
          });
        } else {
          const data = this.state[selectedType];
          if (data.indexOf(id) === -1) {
            //push id
            updatedData = [...data, id];
          } else {
            updatedData = data.filter((value) => value !== id);
          }
        }

        if(type) {
          this.setState({
            [selectedType]: updatedData,
            refreshData: true
          });
        }
      };

      render() {
        return (
          <component
            selectedCountries={this.state.selectedCountries}
            selectedLocations={this.state.selectedLocations}
            refreshData={this.state.refreshData}
          />
        );
      }
    }

但是它们从app.js状态传递到component.js作为props。所以它们会成为comp.js中的props,对吧?无论如何,我尝试了以下操作,但不起作用... if ( this.state.selectedCountries !== prevState.selectedCountries ) { if ( this.props.selectedCountries ) console.log('更新选定的国家'); } if ( this.state.selectedLocations !== prevState.selectedLocations ) { console.log('更新选定的位置'); } - barrylachapelle
我的错,检查一下更新的帖子吧。由于它们指向相同的引用,你所拥有的条件将总是评估为真。 - Sushanth --
哎呀,这是一个很好的答案,但对我仍然没有用 :( - barrylachapelle
你是如何向数组中添加新项的? - Sushanth --
没有查看设置状态的代码,很难说。 - Sushanth --
显示剩余2条评论

1

你确定位置和国家的道具实际上已经更改了吗?如果是,以下代码应该有效:

componentDidUpdate(prevProps) {
  if (this.props.selectedCountries.length !== prevProps.selectedCountries.length) {
    console.log("updated selected countries");
  }

  if (this.props.selectedLocations.length !== prevProps.selectedLocations.length) {
    console.log("updated selected locations");
  }
}

我创建了一个演示效果的代码片段,链接如下: https://codesandbox.io/s/o580n8lnv5


0

我也遇到了这个问题。我的解决方法是向子组件发送一个有关状态的克隆。这样,当在App.js中再次更改状态时,它不会影响传递给子组件的状态副本,因为那些子组件被赋予了一个克隆。在子组件中之前传递给async componentDidUpdate (prevProps)的上一个属性prevProps将是最初传递下来的副本,而当前属性将是在App.js中进行的最新状态更改,同样是一个副本,但前一个和当前的属性将不同。

以下是从App.render()剪切出来的片段,注意分配给filter属性的值,即状体一部分的克隆:

...
<Routes onCategorySelect={this.handleCategorySelect}
                onCategoryUnselect={this.handleCategoryUnselect}
                onRouteLoad={this.handleRouteLoad} filter={this.cloneFilter(savedState.filter)}
                updatedDimension={this.state.updatedDimension}/>
...

这是子组件的 componentDidUpdate()

async componentDidUpdate (prevProps) {
    if (this.props.filter !== prevProps.filter && this.props.updatedDimension !== this.dimension) {
      await this.updateChart()
    }
  }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接