Как вставить элемент внутрь массива useState React hook? Это старый метод в состоянии реакции? Или что-то новое?
Например. Пример push-уведомления setState ?



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Когда вы используете 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>@Aprillion - Не совсем уверен, что ты хочешь сказать этим useEffect примером...?
Если useEffect имеет пустой список зависимостей [], он будет выполнен только во время первого рендера. Таким образом, значение theArray внутри эффекта всегда будет initialArray. В ситуациях, когда setTheArray([...initialArray, newElement]) не имеет смысла, а theArray константа всегда будет равна initialValue, необходимы функциональные обновления.
@Aprillion - Боюсь, я все еще не понимаю, возможно, я немного туговат. :-) Зачем нужен эффект только со значением массива исходный? (Я имею в виду, что может быть нечастый вариант использования, но...) И даже если вы это сделаете, какое это имеет отношение к вопросу?
Привет, @T.J.Crowder, я использую метод обратного вызова для объединения старых данных с новыми при извлечении данных, но по какой-то причине он не работает должным образом, не могли бы вы проверить это, чтобы понять мою проблему?
Я изо всех сил пытался добавить некоторую ценность к существующей ценности. Спасибо!
Есть ли способ сделать это без накладных расходов на копирование массива каждый раз?
@AlwaysLearning - Официально нет. :-) Вы не можете напрямую изменять объекты, находящиеся в состоянии React. С очень, очень тщательное тестирование, который вы повторяете в каждом второстепенном выпуске React, вам может сойти с рук прямое обновление, за которым следует A) вызов forceUpdate (в компоненте класса) или B) изменение какого-либо другого члена состояния (для запуска повторного рендеринга) . Но любой компонент, использующий массив, который оптимизируется, не выполняя повторную визуализацию, когда массив не меняется (memo,...
...PureComponent и т. д.) будет сбит с толку. Или вы можете играть в игры с Proxy (где вы заменяете Proxy, а не копируете массив) и предоставлять прокси всему, что его использует. Я не удивлюсь, если это окажется больше накладных расходов, а не меньше, чем просто копирование массива.
Точно так же вы делаете это с «нормальным» состоянием в компонентах класса 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>
);
}
это будет работать с onClick, но setState(oldState => [...oldState, pushsomething]) - это то, как вы должны это сделать
Чтобы немного расшириться, вот несколько распространенных примеров. Начиная с:
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-r991u
Спасибо за все примеры. У меня была проблема именно с вашим Вставьте элемент в конец объекта массивов. Я пробовал так много разных вещей, но просто не мог собрать их воедино.
Использование prevState устранило мою проблему, заключавшуюся в том, что только последний вызов применялся к одновременному объединению и фильтрации массива.
setTheArray([...theArray, newElement]); — самый простой ответ, но будьте осторожны с изменением элементов в массив. Используйте глубокое клонирование элементов массива.
Упомянутое вами глубокое клонирование означает получение как objA. UseState и объединить objA с objB? Так же, как эта ссылка? gist.githubusercontent.com/kahsing/…
Наиболее рекомендуемый метод — совместное использование функции-оболочки и оператора распространения. Например, если вы инициализировали состояние под названием name вот так,
const [names, setNames] = useState([])
Вы можете нажать на этот массив следующим образом:
setNames(names => [...names, newName])
Надеюсь, это поможет.
Пожалуйста, не публикуйте ответы только по ссылке. Ссылка может быть сломана в будущем. Вместо этого создайте описание этой статьи и обязательно ответьте на вопрос.
Хотя эта ссылка может ответить на вопрос, лучше включить сюда основные части ответа и предоставить ссылку для справки. Ответы, содержащие только ссылки, могут стать недействительными, если связанная страница изменится. - Из обзора
// 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
Я попробовал описанные выше методы для помещения объекта в массив объектов в useState, но при использовании Машинопись возникла следующая ошибка:
Type 'TxBacklog[] | undefined' must have a 'Symbol.iterator' method that returns an 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]);
если вы хотите нажать после определенного индекса, вы можете сделать следующее:
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;
});
};
Вы можете добавить массив данных в конце пользовательского состояния:
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]);
Создание состояния массива с помощью useState() Во-первых, давайте посмотрим, как использовать хук useState() для создания переменной состояния массива.
import React from "react";
const { useState } = React;
const [myArray, setMyArray] = useState([]);
Однако с React нам нужно использовать метод, возвращаемый из useState, для обновления массива.
setMyArray(oldArray => [...oldArray, newElement]);
Аргумент типа '(oldArray: { array1: string[]; }) => any[]' нельзя присвоить параметру типа 'SetStateAction<{ array1: string[]; }>'. Тип '(oldArray: { array1: string[]; }) => any[]' не может быть присвоен типу '(prevState: { array1: string[]; }) => { array1: string[]; }'. Свойство 'array1' отсутствует в типе 'any[]', но необходимо в типе '{ array1: string[]; }'.ts(2345)
...и попробуйте
setTheArray(currentArray => [...currentArray, newElement]), если вышеперечисленное не работает. Скорее всего вuseEffect(...theArray..., [])важно понимать, чтоtheArrayэто константа, поэтому функциональные обновления нужны для значений из будущих рендеров