如何使用 useState 钩子按索引更新数组?

时间:2020-12-21 13:27:51

标签: javascript reactjs typescript redux react-redux

我有一个选择组件,我想用它来更新基于索引的对象数组的值。

我这样使用钩子:

const [areas, setAreas] = useState(product.areas);

这将返回以下“区域”:

[
  0: {de: "Getraenke", en: "Drinks"},
  1: {de: "Snacks", en: "Snacks"}
]

我的选择组件如下所示:

{areas?.map((area: {de: string, en: string}, index: number) => (
  <Wrapper key={index}>
    <Select
      label="Area"
      name="product-area"
      value={area[lang] || ''}
      onChange={e => setArea([...area, { [lang]: e.target.value }])}
    >
      {areaOptions.map(option => (
        <option value={option} key={option}>
          {option}
        </option>
      ))}
    </Select>
  </InputWrapper>
))}

通过这种方式,我很遗憾地在选择一个选项(这里是“甜点”)后得到以下“区域”:

[
  0: {de: "Getraenke", en: "Drinks"},
  1: {de: "Snacks", en: "Snacks"}
  2: {en: "Sweets" }
]

所以数组的第一个对象没有按预期更新,而是附加了另一个对象,不幸的是,它也缺少“de”语言。

我的目标是更新数组的第一个对象(或基于选择组件索引的对象),而不是:

  0: {de: "Getraenke", en: "Drinks"}

..更新后的对象如下所示:

  0: {de: "Suessigkeiten", en: "Sweets"}

因此,应根据当前选择组件的索引更新数组中的对象,并考虑所选语言(默认为“en”)。

2 个答案:

答案 0 :(得分:1)

让我们一步一步解决这个问题

1.附加了另一个对象

那是因为你告诉它这样做:)

onChange={e => setArea([...area, { [lang]: e.target.value }])}

这意味着,复制整个数组 ...area 并在末尾添加一个新对象 { [lang]: e.target.value }

您犯的另一个错误是没有使用回调函数,访问当前 ...area 的正确方法应该是:

onChange={e => setArea((currentArea) => [...currentArea, { [lang]: e.target.value }]}

这样您就始终拥有最新版本的 area

现在,要通过索引更新特定对象,您可以执行以下操作:

onChange={e => setArea((currentArea) => {
  const { value } = e.target;
  
  const newArea = currentArea.map((singleArea, areaIndex) => {
    if (areaIndex === index) {
      return { [lang]: value }
    }
 
    return singleArea;
  });

  return newArea;
}}

2.缺乏“de”语言

同样,您明确地只向其中添加一种语言:)

{ [lang]: value } // { en: 'something' }

不确定如何为您解决这个问题,但至少您可以理解为什么它是错误的,它应该是这样的(只是一个概念,这行不通):

{ [langDe]: value, [langEn]: value } // { de: 'Guten Tag', en: 'something' }

答案 1 :(得分:1)

最简单的方法是克隆数组,通过索引更新特定的数组项,然后使用 useState 用它替换旧数组,就像这样。

const updateArea = (e, lang, index) => {
  const updatedAreas = [...areas];
  updatedArea[index][lang] = e.target.value;
  setAreas(updatedAreas);
}

...

{areas?.map((area: {de: string, en: string}, index: number) => (
  <Wrapper key={index}>
    <Select
      label="Area"
      name="product-area"
      value={area[lang] || ''}
      onChange={e => updateArea(e, lang, index)}
    >
      {areaOptions.map(option => (
        <option value={option} key={option}>
          {option}
        </option>
      ))}
    </Select>
  </InputWrapper>
))}