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

Øyv*_*tad 32 reactjs

我创建了一个React组件,它给出了一组给定一组id的子元素.id数组保持在父组件的状态,然后我根据id运行一些ajax调用来获取要呈现的数据.获取的数据存储在该状态的单独数据阵列中.呈现的组件使用id作为键.

id可以根据组件外部的操作进行更改,因此我在组件上使用setState来替换数组.更新的id-state可能包含一些与原始数组中相同的ID. 同时我清空'数据数组',以便再次渲染所有内容.

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

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

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

编辑:按要求添加了一些代码.注意:我使用的是Infinite Scroll模块.这会导致它吗?

初始状态:

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

渲染功能:

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

简化加载更多:

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

最后在组件外部进行更新时:

movieBox.setState({
  hasMore: true,
  num: 0,
  movieIds: filteredIds,
  movies: []
});
Run Code Online (Sandbox Code Playgroud)

Øyv*_*tad 20

我弄清楚了我的错误,它与React本身无关.这是一个在循环缺少javascript闭包的经典案例.

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

在React Inifinite 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);
      }
    });  
  }    
Run Code Online (Sandbox Code Playgroud)

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

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


Mik*_*ver 12

我不会将后端的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>
    );
}
Run Code Online (Sandbox Code Playgroud)

  • 例外情况是项目可以重新排序,或者除了结尾之外的任何地方插入/删除. (6认同)