Mam następujący stan:
this.setState({ selected: { id: 1, name: 'Foobar' } });
Następnie aktualizuję stan:
this.setState({ selected: { name: 'Barfoo' }});
Ponieważ setState
ma nastąpić scalenie, spodziewałbym się, że będzie to:
{ selected: { id: 1, name: 'Barfoo' } };
Ale zamiast tego zjada identyfikator, a stan to:
{ selected: { name: 'Barfoo' } };
Czy to oczekiwane zachowanie i jakie jest rozwiązanie polegające na aktualizowaniu tylko jednej właściwości zagnieżdżonego obiektu stanu?
źródło
this.setState({ selected: { ...this.state.selected, name: 'barfoo' } })
który dostaje przeliczonych nathis.setState({ selected: _extends({}, this.state.selected, { name: 'barfoo' }) });
underscore
/lodash
. Proszę przekształcić to we własną odpowiedź.this.state
niekoniecznie aktualne . Możesz uzyskać nieoczekiwane rezultaty używając metody rozszerzania. Przekazanie funkcji aktualizacji dosetState
jest poprawnym rozwiązaniem.Pomocnicy niezmienności zostali niedawno dodani do React.addons, więc dzięki temu możesz teraz zrobić coś takiego:
Dokumentacja pomocników niezmienności .
źródło
React.addons.update()
metoda jest przestarzała, ale zastępcza biblioteka github.com/kolodny/immutability-helper zawiera równoważnąupdate()
funkcję.Ponieważ wiele odpowiedzi wykorzystuje bieżący stan jako podstawę do scalania nowych danych, chciałem zwrócić uwagę, że może to się zepsuć. Zmiany stanu są umieszczane w kolejce i nie powodują natychmiastowej modyfikacji obiektu stanu składnika. Odwołanie się do danych stanu przed przetworzeniem kolejki da zatem nieaktualne dane, które nie odzwierciedlają oczekujących zmian wprowadzonych w setState. Z dokumentów:
Oznacza to, że używanie „bieżącego” stanu jako odniesienia w kolejnych wywołaniach funkcji setState jest zawodne. Na przykład:
Jeśli potrzebujesz użyć bieżącego stanu (np. Aby scalić dane w zagnieżdżony obiekt), setState alternatywnie akceptuje funkcję jako argument zamiast obiektu; funkcja jest wywoływana po wszelkich poprzednich aktualizacjach stanu i przekazuje stan jako argument - można to więc wykorzystać do wprowadzenia atomowych zmian gwarantujących uwzględnienie poprzednich zmian.
źródło
this.state
, który może być nieaktualny (lub raczej oczekująca aktualizacja w kolejce), gdy uzyskujesz do niego dostęp.Nie chciałem instalować kolejnej biblioteki, więc oto kolejne rozwiązanie.
Zamiast:
Zrób to zamiast tego:
Lub, dzięki @ icc97 w komentarzach, jeszcze bardziej zwięźle, ale prawdopodobnie mniej czytelnie:
Ponadto, aby było jasne, ta odpowiedź nie narusza żadnego z obaw, o których @bgannonpl wspomniał powyżej.
źródło
this.setState({ selected: Object.assign(this.state.selected, { name: "Barfoo" }));
Object.assign(a, b)
pobiera atrybuty zb
, wstawia / nadpisuje jea
i zwracaa
. Reaguj, aby ludzie byli zarozumiali, jeśli bezpośrednio zmodyfikujesz stan.{}
jako pierwszy argument:this.setState({ selected: Object.assign({}, this.state.selected, { name: "Barfoo" }) });
. Nie zmienia to pierwotnego stanu.Zachowanie poprzedniego stanu na podstawie odpowiedzi @bgannonpl:
Przykład Lodash :
Aby sprawdzić, czy działa poprawnie, możesz użyć wywołania zwrotnego funkcji drugiego parametru:
Użyłem,
merge
ponieważextend
w przeciwnym razie odrzuca inne właściwości.Przykład niezmienności reakcji :
źródło
Moim rozwiązaniem tego rodzaju sytuacji jest użycie, tak jak wskazała inna odpowiedź, pomocników niezmienności .
Ponieważ ustawienie stanu dogłębnego jest powszechną sytuacją, stworzyłem następującą mieszankę:
Ta mieszanka jest zawarta w większości moich składników i generalnie nie używam
setState
już bezpośrednio.Dzięki takiemu miksowi wszystko, co musisz zrobić, aby osiągnąć pożądany efekt, to wywołać funkcję
setStateinDepth
w następujący sposób:Po więcej informacji:
setStateinDepth
zobaczyć dokumentację Immutability Helpers .źródło
this.state.test.two
będzie tam nadal po aktualizacjithis.state.test.one
za pomocąsetStateInDepth({test: {one: {$set: 'new-value'}}})
. Używam tego kodu we wszystkich moich komponentach, aby obejść ograniczonąsetState
funkcję Reacta .this.state.test
jako Array. Zmiana na Obiekty, rozwiązana.Używam klas es6 i skończyłem z kilkoma złożonymi obiektami na moim najwyższym stanie i próbowałem uczynić mój główny komponent bardziej modułowym, więc stworzyłem prostą otokę klasy, aby zachować stan na najwyższym komponencie, ale pozwolić na bardziej lokalną logikę .
Klasa opakowująca przyjmuje funkcję jako konstruktor, która ustawia właściwość stanu głównego składnika.
Następnie dla każdej złożonej właściwości w stanie głównym tworzę jedną klasę StateWrapped. Możesz tutaj ustawić domyślne właściwości w konstruktorze, które zostaną ustawione, gdy klasa zostanie zainicjowana, możesz odwołać się do stanu lokalnego w celu uzyskania wartości i ustawić stan lokalny, odwołać się do funkcji lokalnych i przekazać je do łańcucha:
Więc mój komponent najwyższego poziomu potrzebuje tylko konstruktora, aby ustawić każdą klasę na jej właściwość stanu najwyższego poziomu, proste renderowanie i wszelkie funkcje, które komunikują się między komponentami.
Wydaje się, że do moich celów działa całkiem nieźle, pamiętaj, że nie możesz zmienić stanu właściwości przypisanych do opakowanych komponentów w komponencie najwyższego poziomu, ponieważ każdy opakowany komponent śledzi swój własny stan, ale aktualizuje stan w górnym komponencie za każdym razem, gdy się zmienia.
źródło
W tej chwili,
zgodnie z dokumentacją https://reactjs.org/docs/react-component.html#setstate , używając:
źródło
Rozwiązanie
Edycja: To rozwiązanie używane do używania składni rozkładów . Celem było stworzenie obiektu bez żadnych odniesień
prevState
, abyprevState
nie był modyfikowany. Ale w moim użyciuprevState
czasami wydawał się modyfikowany. Tak więc, aby uzyskać doskonałe klonowanie bez skutków ubocznych, teraz konwertujemyprevState
na JSON, a potem z powrotem. (Inspiracja do korzystania z formatu JSON pochodzi z MDN ).Zapamiętaj:
setState(prevState => stateChange)
Kroki
state
które chcesz zmienićKroki 3 i 4 można łączyć w jednej linii.
Przykład
Uproszczony przykład:
Jest to zgodne z wytycznymi React. Na podstawie odpowiedzi Eicksla na podobne pytanie.
źródło
Rozwiązanie ES6
Stan początkowo ustawiamy
Zmieniamy właściwość na pewnym poziomie obiektu stanu:
źródło
Stan React nie wykonuje scalania rekurencyjnego,
setState
podczas gdy oczekuje, że w tym samym czasie nie będą dostępne lokalne aktualizacje elementu członkowskiego. Musisz albo samodzielnie skopiować zamknięte obiekty / tablice (za pomocą array.slice lub Object. assign), albo skorzystaj z dedykowanej biblioteki.Jak ten. NestedLink bezpośrednio obsługuje obsługę złożonego stanu reakcji .
Ponadto łącze do
selected
lubselected.name
można przekazać wszędzie jako pojedynczy element i zmodyfikować tam za pomocąset
.źródło
czy ustawiłeś stan początkowy?
Użyję własnego kodu, na przykład:
W aplikacji, nad którą pracuję, tak ustawiam i używam stanu. Wierzę,
setState
że możesz wtedy po prostu edytować dowolne stany indywidualnie.Nazywałem to tak:Pamiętaj, że musisz ustawić stan w
React.createClass
wywołanej funkcjigetInitialState
źródło
Używam tmp var do zmiany.
źródło
let original = {a:1}; let tmp = original; tmp.a = 5; // original is now === Object {a: 5}
Nie rób tego, nawet jeśli nie spowodowało to jeszcze problemów.