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


Відповіді:


312

Коли ви використовуєте useState, ви можете отримати метод оновлення для елемента стану:

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

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

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

Іноді ви можете обійтися без використання цієї форми зворотного виклику, якщо ви тільки оновити масив в обробниках для деяких конкретних користувальницьких подій , таких як click(але не так, як mousemove):

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

Події, для яких React забезпечує змивання рендерингу, - це "дискретні події", перелічені тут .

Приклад у реальному часі (передача зворотного виклику в setTheArray):

Оскільки єдиним оновленням є оновлення theArrayу clickподії (одна з "дискретних" подій), я міг би піти від прямого оновлення в addEntry:


1
... і спробуйте, setTheArray(currentArray => [...currentArray, newElement])якщо вищезазначене не працює. Швидше за все useEffect(...theArray..., []), важливо розуміти, що theArrayце константа, тому для значень із майбутніх
рендерів

@Aprillion - Не зовсім впевнений, що ви говорите на цьому useEffectприкладі ...?
TJ Crowder,

1
Якщо список useEffectмістить порожній список залежностей [], він буде виконаний лише під час першого візуалізації. Тож значення theArrayвнутрішнього ефекту буде завжди initialArray. У ситуаціях, коли setTheArray([...initialArray, newElement])не має сенсу і коли theArrayконстанта завжди буде дорівнює, необхідні initialValueфункціональні оновлення.
Квітень

@Aprillion - боюсь, я все ще не отримую його, можливо, трохи щільний. :-) Чому вам потрібен ефект лише з початковим значенням масиву? (Я маю на увазі, може бути рідкісний випадок використання, але ...) І навіть якщо ви це зробите, яке відношення це має до питання?
TJ Crowder,

1
Чоловіче, я боровся з цим найдовше. Я думав setTheArray(()=>theArray.concat(newElement)), спрацює, але НІ! Цей параметр зворотного виклику робить ВСЮ РІЗНИЦЮ.
Джеф Лоуері,

52

Щоб розширити трохи далі, ось кілька типових прикладів. Починаючи з:

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

Натисніть елемент в кінці масиву

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

Елемент push / update в кінці об'єкта

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

Елемент push / update в кінці масиву об'єктів

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


5
Дякую за всі приклади. Точно, з чим у мене виникала проблема, це ваш елемент Push в кінці об’єкта масивів . Я пробував стільки різних речей, але просто не міг це скрутити.
подвійний

1
Використання prevState виправило мою проблему, оскільки лише останній виклик застосовувався до одночасного масиву concat і фільтра.
Генріке Бруно,

5

Так само, як ви робите це з "нормальним" станом у компонентах класу 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>
  );
}

1
це буде працювати з onClick, але setState (oldState => [... oldState, pushsomething]) - це те, як ви повинні це робити
Cyrus

3
// 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


2

setTheArray([...theArray, newElement]);- це найпростіша відповідь, але будьте обережні щодо мутації елементів у масиві . Використовуйте глибоке клонування елементів масиву.


Глибоке клонування, про яке ви згадали, означає, що отримуєте як objA. UseState і зв’язати objA з objB? Те саме, що це посилання? gist.githubusercontent.com/kahsing/…
Луї

1

Найбільш рекомендованим методом є використання функції обгортки та оператора поширення разом. Наприклад, якщо ви ініціалізували такий стан name,

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

Ви можете натиснути на цей масив так,

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

Сподіваюся, що це допомагає.


1
Будь ласка, не публікуйте відповіді лише на посилання. Посилання може бути порушено в майбутньому. Натомість створіть матеріал цієї статті та переконайтеся, що ви насправді відповіли на запитання.
BlackCetha,

Хоча це посилання може відповісти на питання, краще включити сюди основні частини відповіді та надати посилання для довідки. Відповіді лише на посилання можуть стати недійсними, якщо пов’язана сторінка зміниться. - З огляду
ilke444

1

якщо ви хочете натиснути на певний індекс, ви можете зробити, як показано нижче:

   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;
        });
    };

1

Ви можете додати масив Даних в кінці нестандартного стану:

  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]);

0

Я спробував описані вище методи для введення об'єкта в масив об'єктів у 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]);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.