在 ReactJs 组件中替换状态时如何避免“具有相同键的子项" [英] How to avoid 'children with same key' when replacing state in ReactJs component
问题描述
我创建了一个 React 组件,它根据给定的 id 数组呈现一组子元素.id 数组保持在父组件的状态,然后我根据 id 运行一些 ajax 调用来获取数据以进行渲染.获取的数据存储在状态中的单独数据数组中.呈现的组件使用 id 作为键.
I have created a React Component that renders a set of sub-elements given an array of ids. The array of ids is kept in the state of the parent component, and then I run some ajax calls based on the ids to fetch data to render. The fetched data is stored in a separate data array in the state. The rendered components use the id as key.
ids 可以根据组件外部的动作而改变,所以我在组件上使用 setState 来替换数组.更新后的 id-state 可能包含一些与原始数组中相同的 id. 同时我清空数据数组",以便再次呈现所有内容.
The ids can change based on actions outside of the component, so I use setState on the component to replace the array. The updated id-state will probably contain some of the same ids as the in the original array. At the same time I empty the 'data array' so that everything will be rendered again.
当我这样做时,我有时会收到关键警告:
When I do this I sometimes get the key-warning:
警告:flattenChildren(...):遇到两个孩子钥匙.子键必须是唯一的;当两个孩子共用一把钥匙时,只有将使用第一个孩子.
Warning: flattenChildren(...): Encountered two children with the same key. Child keys must be unique; when two children share a key, only the first child will be used.
新数组不包含任何重复项.那么为什么会发生这种情况,我该怎么做才能避免这种情况呢?
The new array does not contain any duplicates. So why does it happen, and what can I do to avoid this?
根据要求添加了一些代码.注意:我正在使用 无限滚动模块.这可能是导致它的原因吗?
Added some code by request. Note: I am using the Infinite Scroll module. Could this be causing it?
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: []
});
推荐答案
我发现了我的错误,它与 React 本身无关.这是循环内缺少javascript闭包的经典案例.
I figured out my mistake, and it had nothing to do with React per se. It was a classic case of missing javascript closure inside a loop.
由于存在重复的可能性,我将每个 ajax 响应存储在 window.localStorage 中的 movieId 上.或者我是这么想的.
Because of the possibility of duplicates I stored each ajax response in window.localStorage, on the movieId. Or so I thought.
在 React Infinite Scroll 中,列表中的每个项目都是通过调用 loadMore 函数按顺序绘制的.在这个函数中,我执行了 ajax 调用,并将结果存储在浏览器缓存中.代码如下所示:
In React Inifinite Scroll each item in your list is drawn sequentially with a call to the loadMore-function. Inside this function I did my ajax call, and stored the result in the browser cache. The code looked something like this:
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 模块调用的 loadMore 函数中完成的,所以我不知道这个函数的作用域不正确.
Can you spot the mistake? When the ajax-call returns, movieId is no longer what is was. So I end up storing the data by the wrong id, and get some strange React warnings in return. Because this was done inside the loadMore function called by the InfiniteScroll-module, I was not aware that this function was not properly scoped.
我通过添加一个立即调用的函数表达式来修复它.
I fixed it by adding a Immediately-invoked function expression.
这篇关于在 ReactJs 组件中替换状态时如何避免“具有相同键的子项"的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!