javascript - 在 React 中从数组中渲染随机元素
问题描述
我在 Potter-API 的帮助下制作了一个小型反应应用程序,用户可以通过它搜索特定的字符或咒语。从 API 获取数据后,我正在渲染 6 个随机项目(字符/咒语),单击它们会导致项目(字符/咒语)的详细视图,我还添加了一个名为 randomize 的按钮,单击该按钮会呈现一个新集合的随机元素。
我面临的问题是这个“随机化”按钮,在反复单击它时发生的事情不是只渲染 6 个元素,而是开始渲染 7、8 ......并且在某些时候中断导致错误。
我想知道是什么原因造成的以及对此的修复。
class RandomItems extends React.Component {
// this.props.randomNums contain the number of random characters to display
// and the max limit of the items (this.props.data.length) and this.props.subUrl contains
// the detailed-view URL(characters or spells) this.props.data is an array of item objects(characters/spells) out of
// which some characters(some = this.props.randomNums) are chosen and rendered by this component
constructor(props) {
super(props);
this.state = {
itemsList: [],
loading: true
}
this.handleRandoms = this.handleRandoms.bind(this)
}
componentDidMount() {
const items = this.getRandomItems()
this.setState({itemsList: items, loading: false})
}
handleRandoms(){
const items = this.getRandomItems()
this.setState({itemsList: items})
}
getRandomItems() {
function getRandomNumbers(num, limit) {
let randoms = []
for (let i = 0; i < num; i++) {
randoms.push(Math.floor(Math.random() * (limit + 1)))
}
return randoms
}
const randoms = getRandomNumbers(this.props.randomNums, this.props.data.length)
return randoms.map(value => this.props.data[value])
}
// Each of the returned character should be a Link to the detail view of that character
// Using the same component for both the spells/characters page so since the object attributes
// are different for both categories I'm using a prop accessKey that is a string(name/spell) for
// accessing the specific attribute based on the item type(character/spell)
render() {
if (this.state.itemsList && !this.state.loading) {
return (
<div style={{marginTop: '6em'}}>
<h2>Have Some Random {(this.props.subUrl)}!</h2>
<br/>
{this.state.itemsList.map((item, index) => {
return (
<div className={'characterDesign'} key={item._id}>
<Link className={'highlight-link'}
to={`/${this.props.subUrl}/${item._id}`}
>
{(index + 1) + '. ' + item[this.props.accessKey]}
</Link>
</div>
)
})}
<button className={'fill'} onClick={this.handleRandoms}>Randomize!</button>
</div>
)
} else {
return (<h1>Loading...</h1>)
}
}
}
所需的数据对象数组从父组件发送
- 在点击了一些随机化后
- 多次点击随机化按钮后
PS。我查看了呈现这些项目的数组,并且每次它都包含 6 个元素(即使正在呈现更多数量的元素)
解决方案
您的getRandomItems
函数可以多次返回同一个项目,因此当 react 渲染项目时,可能会有多个相同的项目_id
(这是因为key
多个项目可以有相同的key
)。
当您有多个<div>
具有相同key
属性的 s 时, react 会感到困惑。的全部意义在于key
独一无二。如果您有多个具有相同的键,则在再次渲染时,react 只会清理最后一个(对于任何给定键)。
这是根本问题的极简示例:
class RandomItems extends React.Component {
constructor(props) {
super(props);
this.state = {
itemsList: [],
loading: true
};
}
componentDidMount() {
const items = [
this.props.data[0],
this.props.data[0],
this.props.data[0]
];
this.setState({
itemsList: items
});
}
onClickTest = () => {
const items = [
this.props.data[1],
this.props.data[2]
];
this.setState({
itemsList: items
});
};
render() {
return (
<div>
{this.state.itemsList.map((item, index) => {
return (
<div key={item.id}>
{item.name}
</div>
)
})}
<button onClick={this.onClickTest}>Test</button>
</div>
)
}
}
/////////////////////////
ReactDOM.render(
<RandomItems randomNums={3} data={[
{id: 0, name: 'Zeroth'},
{id: 1, name: 'First'},
{id: 2, name: 'Second'}
]}></RandomItems>,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
请注意,当您单击“测试”时,三个“0 Zeroth”div 中的最后一个被删除(应该如此),但其他两个没有(因为 react 不期望多个 div 具有相同的 div key
)。
在您的情况下,最好的解决方案可能是修复您的 randomize 函数,这样它就不会多次返回相同的项目。例子:
getRandomItems = () => {
let allItems = [...this.props.data];
const randomCount = this.props.randomNums;
const randomItems = [];
for (let i = 0; i < randomCount; i++) {
const randomIndex = Math.floor(Math.random() * allItems.length);
const randomItem = allItems.splice(randomIndex, 1)[0];
randomItems.push(randomItem);
}
return randomItems;
};
或者,您可以将key
from更改item._id
为index
也可以解决问题,因为index
永远是唯一的。
推荐阅读
- android - 任何人都可以查看我的 AndroidSQLite 查询游标吗?
- ruby - 如何在使用carrierwave gem上传文件时修复“未定义的方法”错误
- c - 在C中交换指针数组中的2个元素(到不同长度的字符串)
- sqlite - 如何在(键,值)sqlite数据库表中插入?
- sql - 如何以“dd/mm/yyyy”格式调整日期
- javascript - 如何创建函数示例 $('#fooinput').barfunction()
- javascript - 在 JavaScript 中使用 array.join()
- typescript - 使用TypeScript,如何强类型mysql查询结果
- react-native - NSMutableDictionary 类型的 JSON 值无法在获取请求时转换为 NSString
- c++ - 如何在 C++ 中将映射中的键和值打印为列表