Метод 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.