React:对象数组的setState更改每个项目

React: setState of array of object changes every item

我正在使用 React 构建一个记忆游戏。我有一组卡片,其中每张卡片可以是 "matching"(用户刚刚点击它)或 "matched"(用户已经找到了两张卡片,它们现在已完全显示)。我的问题是,当我尝试使用 setState 设置匹配状态时,每张卡片的状态都会发生变化,而不仅仅是被点击的卡片。这是我拥有的:

import React from 'react';
import ReactDOM from 'react-dom';
import Card from './card';

import './index.css';

class Game extends React.Component {
    constructor() {
        super();
        this.state = {
            cards: Array(4).fill(
                {
                    matching: false,
                    matched: false,
                }
            ),
            currentlyMatching: false,
        }
    }

    handleCardClick(index, type) {
        let currentCardsState = this.state.cards.slice();
        currentCardsState[index].matching = true;
        this.setState({cards: currentCardsState});
    }

    renderCard(index, type) {
        return <Card value={type}
        matching={this.state.cards[index].matching}
        matched={this.state.cards[index].matched} // this line is the issue
        onClick={() => this.handleCardClick(index, type)}
        />;
    };

    render() {
        return <div id="game">
            {this.renderCard(0,"red")}
            {this.renderCard(1, "green")}
            {this.renderCard(2, "red")}
            {this.renderCard(3, "green")}
        </div>;
    };
}

ReactDOM.render(
    <Game />,
    document.getElementById('root')
);

这正是预期的结果:您在状态中设置了一个新的对象数组,因此会为每个对象再次调用渲染

您遇到的问题是您没有为每张卡片创建 4 个独立的对象。您正在创建 one 对象,该对象在数组中出现了四次。这意味着更改任何索引都会影响所有索引。

这就是 Array.fill 的工作方式。

要创建四个独立的状态,你需要这样的东西:

const cards = [];
for(var i = 0; i < 4; i++) {
  cards.push({
    matched: false,
    matching: false
  ));
}

您可以在每个Card组件中添加一个shouldComponentUpdate以防止不必要的re-renders:

shouldComponentUpdate(prevProps, prevState) {
    return prevProps !== this.props
}

或者您可以专门针对单个道具:

shouldComponentUpdate(prevProps, prevState) {
    return prevProps.matched !== this.props.matched
}