从React中的数组渲染随机元素

时间:2020-08-06 09:37:52

标签: javascript reactjs react-router

我正在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>)
        }
    }
}

所需的数据对象数组是从父组件发送的

  1. 点击几次随机后 After some clicks of the randomize
  2. 多次单击随机按钮后 After many clicks of the randomize button

PS。我已经看过了呈现这些项目的数组,并且每次它恰好包含6个元素时(即使正在呈现更多数量的元素)

1 个答案:

答案 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将始终是唯一的。