Обновление массива в React с помощью хуков

Я пытаюсь понять, как работает React Hook API. Я пытаюсь добавить номер в список. Код, который я прокомментировал, то есть myArray.push ... похоже, не выполняет операцию, хотя приведенный ниже код работает нормально. Почему это так?

import React, {useState} from 'react'

export default () => {

  const [myArray, setArray] = useState([1,2,3])

  return (
    <div>
      {myArray.map((item=>{

        return <li>{item}</li>

      }))}
      <button onClick = {()=>{

        // myArray.push(myArray[myArray.length-1]+1)
        // setArray(myArray)

        setArray([...myArray, myArray[myArray.length-1]+1])

      }}>Add</button>
    </div>
  )
}

person Jithin Ks    schedule 19.02.2019    source источник
comment
Есть ли причина, по которой вы пытаетесь реализовать компонент с отслеживанием состояния в качестве функционального компонента?   -  person CerebralFart    schedule 19.02.2019
comment
@SZenC Почему бы и нет, теперь, когда существуют хуки?   -  person Nicholas Tower    schedule 19.02.2019
comment
Я считаю, что myArray это this.state.myArray, и, как обычно, вам не следует изменять состояние, поэтому раньше вы никогда не this.state.myArray.push(....), что также означает, что вам не следует пытаться myArray.push сейчас   -  person Isaac    schedule 19.02.2019
comment
[...myArray, myArray[myArray.length-1]+1] !== myArray, что приведет к повторному рендерингу компонента.   -  person Tholle    schedule 19.02.2019
comment
@Tholle В этом есть смысл, поэтому React сравнивает существующий myArray и то, что мы передаем, и если есть изменение, перерисовывает? И в первом случае я передаю сам myArray в setArray, поэтому рендеринга нет?   -  person Jithin Ks    schedule 19.02.2019
comment
@JithinKs Верно. React выполнит строгое === сравнение под капотом, и, поскольку myArray.push просто добавляет еще один элемент в существующий массив, он не будет повторно визуализироваться. [...myArray, myArray[myArray.length-1]+1] однако создает совершенно новый массив. Если вы будете придерживаться практического правила не изменять состояние напрямую, вы не столкнетесь с этими проблемами.   -  person Tholle    schedule 19.02.2019


Ответы (3)


Я бы рекомендовал использовать useReducer для чего-нибудь более сложного, чем одно значение.

function App() {
  const [input, setInput] = useState(0);

  const [myArray, dispatch] = useReducer((myArray, { type, value }) => {
    switch (type) {
      case "add":
        return [...myArray, value];
      case "remove":
        return myArray.filter((_, index) => index !== value);
      default:
        return myArray;
    }
  }, [1, 2, 3]);

  return (
    <div>
      <input value={input} onInput={e => setInput(e.target.value)} />
      <button onClick={() => dispatch({ type: "add", value: input})}>
        Add
      </button>

      {myArray.map((item, index) => (
        <div>
          <h2>
            {item}
            <button onClick={() => dispatch({ type: "remove", value: index })}>
              Remove
            </button>
          </h2>
        </div>
      ))}
    </div>
  );
}
person K..    schedule 19.02.2019

Вы не изменяете массив в коде комментирования, и, следовательно, когда вы пытаетесь setState, хуки внутренне проверяют, что такое же состояние передается из-за того, что ссылка не обновляется для myArray, и, следовательно, не будет запускать повторную визуализацию снова.

Однако в рабочем коде вы создаете новый экземпляр массива и, следовательно, обновление работает правильно.

person Shubham Khatri    schedule 19.02.2019

Вы можете использовать способ глубокое клонирование объекта и использование его в качестве временной переменной для изменения значения вашего массива.

Вот пример с вашим кодом:

import React, { useState } from 'react'

export default () => {
  const [myArray, setArray] = useState([1, 2, 3])
  var tmpArray = JSON.parse(JSON.stringify(myArray))

  return (
    <div>
      {myArray.map((item) => {
        return <li>{item}</li>
      })}
      <button
        onClick={() => {
          tmpArray.push(tmpArray[tmpArray.length - 1] + 1)
          setArray(tmpArray)
        }}
      >
        Add
      </button>
    </div>
  )
}
person Simon Gagnon    schedule 26.11.2020