在 ReactJs 组件中替换状态时如何避免“具有相同键的子项”

IT技术 reactjs
2021-05-08 10:19:35

我创建了一个 React 组件,它在给定一组 id 的情况下呈现一组子元素。id 数组保持在父组件的状态,然后我根据 id 运行一些 ajax 调用来获取数据以进行渲染。获取的数据存储在状态中的单独数据数组中。渲染的组件使用 id 作为 key

id 可以根据组件外部的操作而改变,所以我在组件上使用 setState 来替换数组。更新后的 id-state 可能包含一些与原始数组中相同的 id。 同时我清空“数据数组”,以便再次呈现所有内容。

当我这样做时,我有时会收到关键警告:

警告:flattenChildren(...):遇到两个具有相同密钥的孩子。子键必须是唯一的;当两个孩子共享一个密钥时,只会使用第一个孩子。

新数组不包含任何重复项。那么为什么会发生这种情况,我该怎么做才能避免这种情况呢?

编辑:按要求添加了一些代码。注意:我正在使用无限滚动module这可能是导致它吗?

初始状态:

getInitialState: function() {
  return {
    hasMore: true,
    num: 0,
    movieIds: this.props.movieIds,
    movies: []
  };
},

渲染功能:

render: function() {
  var InfiniteScroll = React.addons.InfiniteScroll;

  return (
    <InfiniteScroll
        pageStart={0}
        loadMore={this.loadMore}
        threshold='20'
        hasMore={this.state.hasMore}>
        <ul className="movieList">
          {this.state.movies}
        </ul>
    </InfiniteScroll>       
);
}

简化加载更多:

comp = this;
$.ajax( {
  url: url,
  contentType: "json",
  success: function (data) {
    var m = createMovieLi(data);
    var updatedMovies = comp.state.movies;
    updatedMovies[num] = m;
    comp.setState({movies: updatedMovies});
  }
});

最后在组件外更新时:

movieBox.setState({
  hasMore: true,
  num: 0,
  movieIds: filteredIds,
  movies: []
});
2个回答

我发现了我的错误,这与 React 本身无关。这是循环缺少 javascript 闭包的经典案例

由于存在重复的可能性,我将每个ajax 响应存储在window.localStorage 中的movieId 上。或者我是这么想的。

在 React Infinite Scroll 中,列表中的每个项目都是通过调用 loadMore 函数按顺序绘制的。在这个函数中,我执行了 ajax 调用,并将结果存储在浏览器缓存中。代码如下所示:

  var cachedValue = window.localStorage.getItem(String(movieId));
  var cachedData = cachedValue ? JSON.parse(cachedValue) : cachedValue;

  if (cachedData != null) {
    comp.drawNextMovie(cachedData);
  } else { 
    $.ajax( {
      type: "GET",
      url: this.state.movieUrl + movieId,
      contentType: "json",
      success: function (movieData) {
        window.localStorage.setItem(String(movieId), JSON.stringify(movieData));
        comp.drawNextMovie(movieData);
      }
    });  
  }    

你能发现错误吗?当 ajax 调用返回时,movieId 不再是原来的样子。所以我最终用错误的 id 存储数据,并得到一些奇怪的 React 警告作为回报。因为这是在 InfiniteScroll module调用的 loadMore 函数内完成的,所以我不知道这个函数的范围不正确。

我通过添加一个立即调用的函数表达式来修复它

我不会将后端的 ID 用作 React 中的关键属性。如果你这样做了,你就依赖于一些离你的组件有点远的逻辑来确保你的键是唯一的。如果它们的键不是唯一的,你可以破坏 react 的渲染,所以这很重要。

这就是为什么在我看来,您应该坚持使用 for 循环或类似方法中的索引来设置关键属性。这样你就知道它们永远不会是非唯一的,这是做到这一点的最简单方法。

如果不确切知道您的 ID 是如何工作的,就不可能说出是什么导致了这里的非唯一冲突。然而,由于key只是为了让 React 能够正确识别元​​素,没有别的,所以除了简单的计数或索引之外,它没有任何意义。

var children = [];
for (var i=0; i<yourArray.length; i++) {
    children.push(
        <div key={i}>{yourArray[i].someProp}</div>
    );
}