【问题标题】:How to change property of one object in an array of objects in React state?如何更改处于 React 状态的对象数组中一个对象的属性?
【发布时间】:2021-04-08 20:19:45
【问题描述】:

我有一个 JavaScript 对象数组,我在 React 状态下保存,单击一下,我更改了数组中一个对象的属性。

我在不改变状态的情况下进行了以下工作,但我当前的 setState() 语法也再次将相同的对象添加到数组的末尾。

如何在状态对象数组中简单地更改其中一个对象的状态,而不添加另一个对象并且不改变状态?

import React, { useState } from 'react';

interface IFlashcard {
    noun: string;
    article: string;
    show: boolean;
}

const initialFlashcards = [
    {
        noun: 'Dependency',
        article: 'die Dependency, die Dependencys',
        show: false
    },
    {
        noun: 'Kenntnis',
        article: 'die Kenntnis, die Kenntnisse',
        show: false
    },
    {
        noun: 'Repository',
        article: 'das Repository, die Repositorys',
        show: false
    },
    {
        noun: 'Kenntnis',
        article: 'die Kenntnis, die Kenntnisse',
        show: false
    }
];

function LanguageFlashcards() {
    const [flashcards, setFlashcards] = useState(initialFlashcards);

    const toggleFlashcard = (flashcard: IFlashcard) => {
        flashcard.show = !flashcard.show;
        setFlashcards([...flashcards, flashcard]);
    }

    return (
        <>
            <h2>Language Flashcards</h2>
            <ul>
                {flashcards.map((flashcard: IFlashcard) => {
                    return (
                        <>
                            <li><span onClick={() => toggleFlashcard(flashcard)}>{flashcard.noun}</span>
                                {flashcard.show && (
                                    <>
                                        : {flashcard.article}
                                    </>
                                )}
                            </li>
                        </>
                    )
                })}
            </ul>
        </>
    );
}

export default LanguageFlashcards;

【问题讨论】:

标签: javascript reactjs state


【解决方案1】:

您的示例实际上是这里的变异状态:

flashcard.show = !flashcard.show;

此时,flashcard 直接引用处于状态的对象,因此更改其属性之一是一种突变。

您需要一种方法来识别处于状态的对象,以便您可以提取一个,单独克隆它,然后将其插入到克隆的状态数组中的原始位置。在不更改任何数据的情况下,您可以通过在调用 toggleFlashcard 时传递 flashcard 的数组位置来做到这一点。

{flashcards.map((flashcard: IFlashcard, i: number) => {
    return (
        <>
            <li><span onClick={() => toggleFlashcard(i)}>{flashcard.noun}</span>
                {flashcard.show && (
                    <>
                        : {flashcard.article}
                    </>
                )}
            </li>
        </>
    )
})}

现在toggleFlashcard 事件处理程序应该如下所示:

const toggleFlashcard = (i: number) => {
    const clonedCard = {...flashcards[i]};
    clonedCard.show = !clonedCard.show;
  
    const clonedState = [...flashcards];
    clonedState[i] = clonedCard;
  
    setFlashcards(clonedState);
}

【讨论】:

    【解决方案2】:

    如果您不想改变任何内容,请尝试此解决方案。

    const toggleFlashcard = (flashcard: IFlashcard) => {
      const flashcardIndex = flashcards.findIndex(f => f === flashcard);
      const newFlashcards = [...flashcards];
      newFlashcards[flashcardIndex]= { ...flashcard, show: !flashcard.show };
      setFlashcards(newFlashcards);
    };
    

    这与主题无关,但这里缺少key 属性。

    {flashcards.map((flashcard: IFlashcard, index: number) => {
    ...
    <li key={index}><span onClick={() => toggleFlashcard(flashcard)}>{flashcard.noun}</span>
                                  
    

    如果你不指定 key 属性,你会看到 React 警告。

    【讨论】:

    • Mutating state in React is a dangerous anti-pattern. 我不建议将其作为任何有关 React 的答案的第一个解决方案。
    • 是的。你是对的。这就是为什么我提供了另一种不改变状态的解决方案。 @EmileBergeron
    • 一个小的改进是避免克隆每个对象,而只克隆正在变化的对象(显然还有数组)。
    • 我根据您的评论更新了我的答案。 @EmileBergeron 它将避免克隆每个对象。
    【解决方案3】:

    要做你想做的事,你可以这样做

    const toggleFlashcard = (flashcardIndex: number) => {
        const flashcardsDeepCopy: IFlashcard[] = JSON.parse(JSON.stringify(flashcards));
        flashcardsDeepCopy[flashcardIndex].show = !flashcard.show;
        setFlashcards(flashcardsDeepCopy);
    }
    

    在jsx中你需要传递索引

    <ul>
      {flashcards.map((flashcard: IFlashcard, index) => {
        return (
          <>
            <li><span onClick={() => toggleFlashcard(index)}>{flashcard.noun}</span>
              {flashcard.show && (
                <>
                  : {flashcard.article}
                </>
              )}
            </li>
          </>
        )
      })}
    </ul>
    

    【讨论】:

      【解决方案4】:

      您的问题在于您的 toggleFlashcard 函数,您认为您没有改变原始状态,但您知道 javascript 通过引用传递您的对象。如果你只是这样做

      const toggleFlashcard = (flashcard: IFlashcard) => {
              flashcard.show = !flashcard.show;
              setFlashcards([...flashcards]);
          }
      

      它会起作用,但这并不是真正的反应练习。您需要做的是在不修改原始对象的情况下返回一个全新的数组。你需要某种标识符来过滤出哪个闪存卡是哪个。可能会将项目的索引附加到渲染器或从后端传递一些东西。

      【讨论】:

        猜你喜欢
        • 2020-10-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-06-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-05-28
        相关资源
        最近更新 更多