我正在Potter-API的帮助下制作一个小型的React应用,用户可以通过该应用搜索特定的字符或咒语。从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个元素时(即使正在呈现更多数量的元素)
答案 0 :(得分:0)
您的getRandomItems
函数可以多次返回相同的项目,因此当react渲染这些项目时,可以有多个具有相同的_id
(被用作{{1} },因此多个项目可以具有相同的key
。
当您拥有多个具有相同key
属性的<div>
时,react会变得混乱。 key
的要点是唯一的。如果您有多个使用相同的键,则只有当再次渲染时,react才会清除最后一个(对于任何给定的键)。
以下是该问题的极简示例:
key
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')
);
当您单击“测试”时,请注意三个“ 0 Zeroth” div中的最后一个已删除(应该如此),而其他两个则未删除(因为react不期望具有相同<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>
的多个div)
在您的情况下,最好的解决方案可能是修复随机函数,以使其永远不会多次返回同一项目。示例:
key
或者,您可以将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
更改为item._id
,这也解决了该问题,因为index
将始终是唯一的。