Metoda push w React hookach (useState)?

125

Jak umieścić element wewnątrz useState array React hook? Czy to stara metoda w stanie reagowania? Czy coś nowego?

Np. Przykład setState push ?

Milosh N.
źródło

Odpowiedzi:

312

Kiedy używasz useState, możesz uzyskać metodę aktualizacji dla elementu stanu:

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

wtedy, gdy chcesz dodać nowy element, używasz tej funkcji i przekazujesz nową tablicę lub funkcję, która utworzy nową tablicę. Zwykle to drugie, ponieważ aktualizacje stanu są asynchroniczne i czasami partiami:

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

Czasami możesz uciec bez korzystania z tego formularza wywołania zwrotnego, jeśli aktualizujesz tablicę tylko w programach obsługi dla pewnych określonych zdarzeń użytkownika, takich jak click(ale nie podobne mousemove):

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

Zdarzenia, dla których React zapewnia, że ​​renderowanie jest opróżnione, to „zdarzenia dyskretne” wymienione tutaj .

Przykład na żywo (przekazywanie wywołania zwrotnego do setTheArray):

Ponieważ jedyną aktualizacją theArrayw tam jest ta w clickzdarzeniu (jedno z „dyskretnych” zdarzeń), mogę uciec z bezpośrednią aktualizacją w addEntry:

TJ Crowder
źródło
1
... i spróbuj, setTheArray(currentArray => [...currentArray, newElement])jeśli powyższe nie działa. Najprawdopodobniej w useEffect(...theArray..., []), ważne jest, aby zdać sobie sprawę, że theArrayjest to stała, więc aktualizacje funkcjonalne są potrzebne dla wartości z przyszłych renderów
kwiecień
@Aprillion - Nie jesteś pewien, co mówisz na tym useEffectprzykładzie ...?
TJ Crowder
1
Jeśli useEffectma pustą listę zależności [], zostanie wykonana tylko podczas pierwszego renderowania. Tak więc wartość theArraywnętrza efektu zawsze będzie initialArray. W sytuacjach, w których setTheArray([...initialArray, newElement])nie miałoby to sensu, a theArraystała zawsze będzie równa initialValue, potrzebne są aktualizacje funkcjonalne.
Kwiecień
@Aprillion - obawiam się, że nadal nie rozumiem, być może jestem trochę gęsty. :-) Dlaczego potrzebujesz efektu zawierającego tylko początkową wartość tablicy? (To znaczy, może zdarzyć się rzadki przypadek użycia, ale ...) A nawet jeśli to zrobisz, co to ma wspólnego z pytaniem?
TJ Crowder
1
Stary, walczyłem z tym najdłużej. Myślałem, setTheArray(()=>theArray.concat(newElement))że zadziała, ale NIE! Ten parametr wywołania zwrotnego stanowi CAŁĄ RÓŻNICĘ.
Jeff Lowery,
52

Aby rozwinąć trochę dalej, oto kilka typowych przykładów. Począwszy od:

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

Wepchnij element na koniec tablicy

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

Wypchnij / zaktualizuj element na końcu obiektu

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

Wypchnij / zaktualizuj element na końcu tablicy obiektów

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

Wepchnij element na koniec obiektu tablic

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

Oto kilka praktycznych przykładów. https://codesandbox.io/s/reacthooks-push-r991u

Elia Ahadi
źródło
5
Dziękuję za wszystkie przykłady. Dokładnie rzecz, z którą miałem problem, był twój element Push na końcu obiektu tablic . Próbowałem tylu różnych rzeczy, ale po prostu nie mogłem tego połączyć.
sdouble
1
Użycie prevState rozwiązało mój problem polegający na tym, że tylko ostatnie wywołanie było stosowane na jednoczesnym konkatowaniu tablic i filtrze.
Henrique Bruno
5

W ten sam sposób robisz to ze stanem „normalnym” w komponentach klasy React.

przykład:

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>
  );
}
rg
źródło
1
to zadziała z onClick, ale setState (oldState => [... oldState, pushsomething]) jest jak powinieneś to zrobić
Cyrus Zei
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

Adarsh ​​Pawar
źródło
2

setTheArray([...theArray, newElement]);to najprostsza odpowiedź, ale uważaj na mutacje elementów w tablicy . Użyj głębokiego klonowania elementów tablicy.

Shivang Gupta
źródło
Czy wspomniane przez Ciebie głębokie klonowanie oznacza otrzymywanie jako objA. UseState i konkatuj objA z objB? To samo co ten link? gist.githubusercontent.com/kahsing/…
Luiey
1

Najbardziej zalecaną metodą jest jednoczesne użycie funkcji opakowującej i operatora rozszerzania. Na przykład, jeśli zainicjowałeś stan nazwany w nameten sposób,

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

Możesz wcisnąć do tej tablicy w ten sposób,

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

Mam nadzieję, że to pomoże.

Khondoker Zahidul Hossain
źródło
1
Prosimy nie publikować odpowiedzi zawierających tylko łącza. W przyszłości łącze może zostać zerwane. Zamiast tego napisz artykuł do tego artykułu i upewnij się, że faktycznie udzielisz odpowiedzi na pytanie.
BlackCetha
Chociaż ten link może odpowiedzieć na pytanie, lepiej jest zawrzeć tutaj zasadnicze części odpowiedzi i podać link do odniesienia. Odpowiedzi zawierające tylko łącze mogą stać się nieprawidłowe, jeśli połączona strona ulegnie zmianie. - Z recenzji
ilke444
1

jeśli chcesz naciskać po określonym indeksie, możesz zrobić, co poniżej:

   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;
        });
    };
Hossein Haji Mali
źródło
1

Możesz dołączyć tablicę danych na końcu stanu niestandardowego:

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

Na przykład, poniżej pojawia się przykład axios:

  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]);
Mohammad Fallah
źródło
0

Wypróbowałem powyższe metody wypychania obiektu do tablicy obiektów w useState, ale podczas korzystania z TypeScript wystąpił następujący błąd :

Wpisz „TxBacklog [] | undefined 'musi mieć metodę' Symbol.iterator ', która zwraca iterator.ts (2488)

Konfiguracja tsconfig.json najwyraźniej była poprawna:

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

To obejście rozwiązało problem (mój przykładowy kod):

Berło:

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

Zmienna stanu:

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

Wypchnij nowy obiekt do tablicy:

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