Метод push в React Hooks (useState)?

Как вставить элемент в массив useState React Hook? Это как старый метод в состоянии реакции? Или что-то новенькое?

Например. Пример push setState?


person Milosh N.    schedule 13.02.2019    source источник


Ответы (9)


При использовании useState можно получить метод обновления для элемента состояния:

const [theArray, setTheArray] = useState(initialArray);

затем, когда вы хотите добавить новый элемент, вы используете эту функцию и передаете новый массив или функцию, которая создаст новый массив. Обычно последнее, поскольку обновления состояния являются асинхронными и иногда пакетными:

setTheArray(oldArray => [...oldArray, newElement]);

Иногда можно обойтись без этой формы обратного вызова, если вы только обновляете массив в обработчиках для определенных конкретных пользовательских событий, таких как click (но не как mousemove):

setTheArray([...theArray, newElement]);

События, для которых React гарантирует, что рендеринг сброшен, - это дискретные события, перечисленные в здесь.

Живой пример (передача обратного вызова в setTheArray):

const {useState, useCallback} = React;
function Example() {
    const [theArray, setTheArray] = useState([]);
    const addEntryClick = () => {
        setTheArray(oldArray => [...oldArray, `Entry ${oldArray.length}`]);
    };
    return [
        <input type="button" onClick={addEntryClick} value="Add" />,
        <div>{theArray.map(entry =>
          <div>{entry}</div>
        )}
        </div>
    ];
}

ReactDOM.render(
    <Example />,
    document.getElementById("root")
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.1/umd/react-dom.production.min.js"></script>

Поскольку единственное обновление для theArray в этом случае есть в событии click (одно из дискретных событий), я мог бы обойтись прямым обновлением в addEntry:

const {useState, useCallback} = React;
function Example() {
    const [theArray, setTheArray] = useState([]);
    const addEntryClick = () => {
        setTheArray([...theArray, `Entry ${theArray.length}`]);
    };
    return [
        <input type="button" onClick={addEntryClick} value="Add" />,
        <div>{theArray.map(entry =>
          <div>{entry}</div>
        )}
        </div>
    ];
}

ReactDOM.render(
    <Example />,
    document.getElementById("root")
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.1/umd/react-dom.production.min.js"></script>

person T.J. Crowder    schedule 13.02.2019
comment
... и попробуйте setTheArray(currentArray => [...currentArray, newElement]), если вышеуказанное не работает. Скорее всего, в useEffect(...theArray..., []) важно понимать, что theArray является константой, поэтому функциональные обновления необходимы для значений из будущих рендеров - person Aprillion; 08.08.2019
comment
@Aprillion - Не совсем понимаю, о чем вы говорите этим useEffect примером ...? - person T.J. Crowder; 09.08.2019
comment
Если useEffect имеет пустой список зависимостей [], он будет выполнен только во время первого рендеринга. Таким образом, значение theArray внутри эффекта всегда будет initialArray. В ситуациях, когда setTheArray([...initialArray, newElement]) не имеет смысла, и когда константа theArray всегда будет равна initialValue, необходимы функциональные обновления. - person Aprillion; 09.08.2019
comment
@Aprillion - Боюсь, я все еще не понимаю, возможно, я немного тупой. :-) Зачем вам нужен эффект только с начальным значением массива? (Я имею в виду, что может быть нечастый вариант использования, но ...) И даже если вы это сделаете, какое это имеет отношение к вопросу? - person T.J. Crowder; 09.08.2019
comment
Привет, @TJCrowder, я использую обратный вызов, чтобы объединить старые данные с новыми при извлечении данных, но по какой-то причине он не работает должным образом, не могли бы вы проверить this, чтобы понять мою проблему? - person Oliver D; 04.09.2020

Чтобы продолжить, вот несколько общих примеров. Начиная с:

const [theArray, setTheArray] = useState(initialArray);
const [theObject, setTheObject] = useState(initialObject);

Вставить элемент в конец массива

setTheArray(prevArray => [...prevArray, newValue])

Вставить / обновить элемент в конце объекта

setTheObject(prevState => ({ ...prevState, currentOrNewKey: newValue}));

Вставить / обновить элемент в конце массива объектов

setTheArray(prevState => [...prevState, {currentOrNewKey: newValue}]);

Вставить элемент в конец объекта массивов

let specificArrayInObject = theObject.array.slice();
specificArrayInObject.push(newValue);
const newObj = { ...theObject, [event.target.name]: specificArrayInObject };
theObject(newObj);

Вот и несколько рабочих примеров. https://codesandbox.io/s/reacthooks-push-k9k5i?file=/src/index.js

person Elia Ahadi    schedule 25.10.2019
comment
Спасибо за все примеры. У меня возникла проблема именно с вашим элементом Push в конце объекта массивов. Я пробовал много разных вещей, но никак не мог соединить их вместе. - person sdouble; 19.01.2020
comment
Использование prevState устранило мою проблему, заключающуюся в том, что к одновременному объединению массивов и фильтру применялся только последний вызов. - person Henrique Bruno; 20.12.2020

Вы можете добавить массив данных в конце настраиваемого состояния:

  const [vehicleData, setVehicleData] = React.useState<any[]>([]);
  setVehicleData(old => [...old, ...newArrayData]);

Например, ниже вы видите пример аксиомов:

  useEffect(() => {
    const fetchData = async () => {
      const result = await axios(
        {
          url: `http://localhost:4000/api/vehicle?page=${page + 1}&pageSize=10`,
          method: 'get',
        }
      );
      setVehicleData(old => [...old, ...result.data.data]);
    };

    fetchData();
  }, [page]);
person Mohammad Fallah    schedule 22.12.2020

Так же, как вы делаете это с «нормальным» состоянием в компонентах класса React.

пример:

function App() {
  const [state, setState] = useState([]);

  return (
    <div>
      <p>You clicked {state.join(" and ")}</p>
      //destructuring
      <button onClick={() => setState([...state, "again"])}>Click me</button>
      //old way
      <button onClick={() => setState(state.concat("again"))}>Click me</button>
    </div>
  );
}
person r g    schedule 13.02.2019
comment
это будет работать с onClick, но setState (oldState = ›[... oldState, pushsomething]) - это то, как вы должны это делать - person Cyrus Zei; 10.12.2019

// Save search term state to React Hooks with spread operator and wrapper function

// Using .concat(), no wrapper function (not recommended)
setSearches(searches.concat(query))

// Using .concat(), wrapper function (recommended)
setSearches(searches => searches.concat(query))

// Spread operator, no wrapper function (not recommended)
setSearches([...searches, query])

// Spread operator, wrapper function (recommended)
setSearches(searches => [...searches, query])

https://medium.com/javascript-in-plain-english/how-to-add-to-an-array-in-react-state-3d08ddb2e1dc

person Adarsh Pawar    schedule 13.05.2020

Наиболее рекомендуемый метод - совместное использование функции оболочки и оператора распространения. Например, если вы инициализировали состояние с именем name, как это,

const [names, setNames] = useState([])

Вы можете нажать на этот массив следующим образом:

setNames(names => [...names, newName])

Надеюсь, это поможет.

person Khondoker Zahidul Hossain    schedule 09.04.2020
comment
Пожалуйста, не публикуйте ответы только по ссылкам. В будущем ссылка может быть разорвана. Вместо этого напишите описание этой статьи и убедитесь, что действительно ответили на вопрос. - person BlackCetha; 09.04.2020
comment
Хотя эта ссылка может дать ответ на вопрос, лучше включить сюда основные части ответа и предоставить ссылку для справки. Ответы, содержащие только ссылки, могут стать недействительными, если ссылка на страницу изменится. - Из отзыва - person ilke444; 13.04.2020

setTheArray([...theArray, newElement]); - самый простой ответ, но будьте осторожны с изменением элементов в theArray. Используйте глубокое клонирование элементов массива.

person Shivang Gupta    schedule 13.12.2019
comment
Упомянутое вами глубокое клонирование означает получение как objA. UseState и объединить objA с objB? То же, что и эта ссылка? githubusercontent.google.com/g/ItemList.js" relser="nofolist.js&hl=ru&&&&&&&&&&&&&&&&&&&&&&& 0&&&&&& 0 1 - person Luiey; 14.08.2020

если вы хотите нажать после определенного индекса, вы можете сделать следующее:

   const handleAddAfterIndex = index => {
       setTheArray(oldItems => {
            const copyItems = [...oldItems];
            const finalItems = [];
            for (let i = 0; i < copyItems.length; i += 1) {
                if (i === index) {
                    finalItems.push(copyItems[i]);
                    finalItems.push(newItem);
                } else {
                    finalItems.push(copyItems[i]);
                }
            }
            return finalItems;
        });
    };
person Hossein Haji Mali    schedule 16.11.2020

Я пробовал указанные выше методы для помещения объекта в массив объектов в useState, но при использовании TypeScript возникла следующая ошибка:

Введите 'TxBacklog [] | undefined 'должен иметь метод' Symbol.iterator ', который возвращает iterator.ts (2488)

Настройка tsconfig.json, по-видимому, была правильной:

{
   "compilerOptions": {
   "target": "es6",
   "lib": [
      "dom",
      "dom.iterable",
      "esnext",
      "es6",
],

Этот обходной путь решил проблему (мой пример кода):

Интерфейс:

   interface TxBacklog {
      status: string,
      txHash: string,
   }

Переменная состояния:

    const [txBacklog, setTxBacklog] = React.useState<TxBacklog[]>();

Вставляем новый объект в массив:

    // Define new object to be added
    const newTx = {
       txHash: '0x368eb7269eb88ba86..',
       status: 'pending'
    };
    // Push new object into array
    (txBacklog) 
       ? setTxBacklog(prevState => [ ...prevState!, newTx ])
       : setTxBacklog([newTx]);
person Sergi Juanati    schedule 13.11.2020