Używam Redux do zarządzania stanem.
Jak zresetować sklep do stanu początkowego?
Załóżmy na przykład, że mam dwa konta użytkowników ( u1
i u2
).
Wyobraź sobie następującą sekwencję zdarzeń:
Użytkownik
u1
loguje się do aplikacji i coś robi, więc buforujemy niektóre dane w sklepie.Użytkownik
u1
wylogowuje się.Użytkownik
u2
loguje się do aplikacji bez odświeżania przeglądarki.
W tym momencie buforowane dane zostaną skojarzone u1
i chciałbym je wyczyścić.
Jak mogę zresetować magazyn Redux do stanu początkowego po wylogowaniu pierwszego użytkownika?
Odpowiedzi:
Jednym ze sposobów na to byłoby napisanie reduktora root w aplikacji.
Reduktor katalogu głównego zwykle przekazuje obsługę działania do generatora reduktora wygenerowanego przez
combineReducers()
. Jednak za każdym razem, gdy odbieraUSER_LOGOUT
akcję, przywraca stan początkowy od nowa.Na przykład, jeśli Twój reduktor root wyglądał tak:
Możesz zmienić nazwę na
appReducer
i napisać nowegorootReducer
delegata:Teraz musimy tylko nauczyć nowego
rootReducer
powrotu do stanu początkowego poUSER_LOGOUT
akcji. Jak wiemy, reduktory powinny zwracać stan początkowy, gdy są wywoływaneundefined
jako pierwszy argument, bez względu na akcję. Wykorzystajmy ten fakt do warunkowego usunięcia nagromadzonego materiału,state
gdy przekazujemy go doappReducer
:Teraz przy każdym
USER_LOGOUT
uruchomieniu wszystkie reduktory zostaną ponownie zainicjowane. Mogą również zwrócić coś innego niż początkowo, jeśli chcą, ponieważ mogą również sprawdzićaction.type
.Aby powtórzyć, pełny nowy kod wygląda następująco:
Zauważ, że nie mutuję tutaj stanu, po prostu ponownie przypisuję odwołanie do zmiennej lokalnej wywołanej
state
przed przekazaniem jej do innej funkcji. Mutowanie obiektu stanu stanowiłoby naruszenie zasad Redux.W przypadku korzystania z funkcji redux-keep może być konieczne wyczyszczenie pamięci. Redux-persist przechowuje kopię twojego stanu w silniku pamięci, a kopia stanu zostanie załadowana stamtąd podczas odświeżania.
Najpierw musisz zaimportować odpowiedni silnik pamięci masowej, a następnie przeanalizować stan przed ustawieniem go
undefined
i wyczyścić każdy klucz stanu pamięci masowej.źródło
case 'CLEAR_DATA': return initialState
export const createRootReducer = asyncReducers => { const appReducer = combineReducers({ myReducer ...asyncReducers }); return (state, action) => { if (action.type === 'LOGOUT_USER') { state = undefined; } return appReducer(state, action); } };
if (action.type === 'RESET') return action.stateFromLocalStorage
USER_LOGOUT
akcji można wcześniej uzyskać dane o stanie? (np. przez devtools)Chciałbym zauważyć, że zaakceptowany komentarz Dana Abramova jest poprawny, z wyjątkiem tego, że podczas korzystania z pakietu React-Router-Redux razem z tym podejściem napotkaliśmy dziwny problem. Nasza poprawka polegała na tym, aby nie ustawiać stanu,
undefined
ale raczej używać obecnego reduktora routingu. Sugeruję więc wdrożenie poniższego rozwiązania, jeśli korzystasz z tego pakietuźródło
router
a NIErounting
combineReducers()
..... gdybyś to zrobiłcombineReducers({routing: routingReducer})
, tak jak opisano w odpowiedziZdefiniuj akcję:
Następnie w każdym ze swoich reduktorów, zakładając, że używasz
switch
lubif-else
do obsługi wielu działań przez każdy reduktor. Zajmę się sprawąswitch
.W ten sposób za każdym razem, gdy wywołasz
RESET
akcję, reduktor zaktualizuje sklep do stanu domyślnego.Teraz, aby się wylogować, możesz wykonać następujące czynności:
Za każdym razem, gdy użytkownik się loguje, bez odświeżania przeglądarki. Sklep zawsze będzie domyślnie.
store.dispatch(RESET_ACTION)
po prostu opracowuje ten pomysł. Najprawdopodobniej będziesz miał do tego celu kreatora akcji. O wiele lepszym sposobem będzie posiadanieLOGOUT_ACTION
.Po wysłaniu tego
LOGOUT_ACTION
. Niestandardowe oprogramowanie pośrednie może następnie przechwycić tę akcję, albo z Redux-Saga lub Redux-Thunk. W obu przypadkach można jednak wysłać inną akcję „RESET”. W ten sposób wylogowanie i zresetowanie sklepu nastąpi synchronicznie, a sklep będzie gotowy na zalogowanie innego użytkownika.źródło
undefined
tak, jak w drugiej odpowiedzi. gdy twoja aplikacja oczekuje drzewa stanu, aundefined
zamiast tego podajesz , jest więcej błędów i problemów niż tylko puste drzewo.Uproszczona odpowiedź na najlepszą odpowiedź:
źródło
Możesz także uruchomić akcję obsługiwaną przez wszystkie lub niektóre reduktory, którą chcesz zresetować do początkowego sklepu. Jedna czynność może wyzerować cały stan lub tylko jego fragment, który wydaje się odpowiedni. Uważam, że jest to najprostszy i najbardziej kontrolowany sposób.
źródło
W przypadku Redux zastosowałem następujące rozwiązanie, które zakłada, że ustawiłem parametr initialState we wszystkich moich reduktorach (np. {Użytkownik: {nazwa, adres e-mail}}). W wielu komponentach sprawdzam te zagnieżdżone właściwości, więc dzięki tej poprawce zapobiegam uszkodzeniu metod renderowania w warunkach połączonych właściwości (np. Jeśli state.user.email, który spowoduje błąd, użytkownik nie zostanie zdefiniowany, jeśli wyżej wspomniane rozwiązania).
źródło
AKTUALIZACJA NGRX4
Jeśli przeprowadzasz migrację do NGRX 4, być może zauważyłeś w przewodniku migracji, że metoda rootreducer do łączenia twoich reduktorów została zastąpiona metodą ActionReducerMap. Na początku ten nowy sposób działania może sprawić, że resetowanie stanu będzie wyzwaniem. W rzeczywistości jest to proste, ale sposób na zmianę tego się zmienił.
To rozwiązanie jest zainspirowane sekcją API meta-reduktorów w dokumentach Github NGRX4.
Po pierwsze, powiedzmy, że łączysz swoje reduktory w ten sposób, używając nowej opcji ActionReducerMap firmy NGRX:
Powiedzmy, że chcesz zresetować stan z poziomu app.module `
`
I to jest w zasadzie jeden sposób na osiągnięcie tego samego efektu za pomocą NGRX 4.
źródło
Łącząc podejście Dana, Ryana i Roba, aby uwzględnić utrzymanie
router
stanu i zainicjowanie wszystkiego innego w drzewie stanu, skończyłem na tym:źródło
Stworzyłem komponent, który daje Redux możliwość resetowania stanu, wystarczy użyć tego komponentu, aby ulepszyć swój sklep i wywołać określone działanie. Typ, aby uruchomić reset. Myśl o wdrożeniu jest taka sama, jak powiedział @Dan Abramov.
Github: https://github.com/wwayne/redux-reset
źródło
Stworzyłem działania, aby wyczyścić stan. Kiedy więc wysyłam twórcę akcji wylogowania, wysyłam również akcje, aby wyczyścić stan.
Akcja zapisu użytkownika
Kreator akcji wylogowania
Reduktor
Nie jestem pewien, czy to jest optymalne?
źródło
Jeśli używasz akcji redux , oto szybkie obejście problemu za pomocą HOF ( funkcja wyższego rzędu )
handleActions
.A następnie użyj
handleActionsEx
zamiast oryginałuhandleActions
do obsługi reduktorów.Odpowiedź Dana daje świetny pomysł na ten problem, ale nie wyszło mi to dobrze, ponieważ używam
redux-persist
.W połączeniu z
redux-persist
zwykłym przejściem wundefined
stan nie spowodowało trwałego zachowania, więc wiedziałem, że muszę ręcznie usunąć element z pamięci (w tym przypadku React NativeAsyncStorage
).lub
też dla mnie nie działały - tylko na mnie krzyczały. (np. skarga typu „Nieoczekiwany klucz _persist ...” )
Potem nagle pomyślałem, że chcę tylko, aby każdy reduktor powrócił do stanu początkowego po
RESET
napotkaniu typu akcji. W ten sposób utrzymywanie się jest obsługiwane w naturalny sposób. Oczywiście bez powyższej funkcji narzędziowej (handleActionsEx
), mój kod nie będzie wyglądał na SUCHY (chociaż jest to tylko jedna linijka, tj.RESET: () => initialState
), Ale nie mogłem tego znieść, ponieważ uwielbiam metaprogramowanie.źródło
Poniższe rozwiązanie działało dla mnie.
Dodałem funkcję resetowania stanu do meta reduktorów. Kluczem było użycie
ustawić wszystkie reduktory do stanu początkowego.
undefined
Zamiast tego powrót spowodował błędy z powodu zniszczenia struktury sklepu./reducers/index.ts
app.module.ts
źródło
Z punktu widzenia bezpieczeństwa, najbezpieczniejszą rzeczą do zrobienia podczas logowania się użytkownika out jest Nast.domyślne uporczywy stan (ex ciasteczka,
localStorage
,IndexedDB
,Web SQL
, itp) i zrobić twarde odświeżenie strony użyciemwindow.location.reload()
. Możliwe jest, że niechlujny programista przypadkowo lub umyślnie przechował niektóre poufne danewindow
w DOM, itp. Usunięcie całego trwałego stanu i odświeżenie przeglądarki to jedyny sposób, aby zagwarantować, że żadne informacje od poprzedniego użytkownika nie zostaną wycierane do następnego użytkownika.(Oczywiście jako użytkownik na współużytkowanym komputerze powinieneś używać trybu „prywatnego przeglądania”, sam zamknij okno przeglądarki, użyj funkcji „wyczyść dane przeglądania” itp., Ale jako programista nie możemy oczekiwać, że wszyscy zawsze będą to sumienne)
źródło
Moje obejście podczas pracy ze skryptem maszynowym, zbudowane na podstawie odpowiedzi Dana (typy redux uniemożliwiają przekazanie
undefined
do reduktora jako pierwszego argumentu, więc buforuję początkowy stan root w stałej):źródło
Przyjęta odpowiedź pomogła mi rozwiązać moją sprawę. Jednak natknąłem się na przypadek, w którym trzeba usunąć całe państwo. Więc - zrobiłem to w ten sposób:
Mam nadzieję, że to pomoże komuś innemu :)
źródło
Po prostu rozszerzenie odpowiedzi @ dan-abramov , czasem może być konieczne zachowanie niektórych kluczy przed zresetowaniem.
źródło
Szybką i łatwą opcją, która działała dla mnie, było użycie resetu redux . Co było proste i zawiera również zaawansowane opcje dla większych aplikacji.
Zainstaluj w sklepie
Wyślij swój „reset” w funkcji wylogowania
Mam nadzieję że to pomoże
źródło
Moje podejście, aby powstrzymać Redux od odwoływania się do tej samej zmiennej stanu początkowego:
źródło
To podejście jest bardzo słuszne: zniszczyć dowolny stan „NAME”, aby zignorować i zatrzymać innych.
źródło
USER_LOGOUT
w tym reduktorze i tam go obsłużyć.dlaczego po prostu nie użyjesz
return module.exports.default()
;)Uwaga: upewnij się, że ustawiłeś domyślną wartość akcji na
{}
i wszystko jest w porządku, ponieważ nie chcesz napotkać błędu podczas sprawdzaniaaction.type
w instrukcji switch.źródło
Stwierdziłem, że zaakceptowana odpowiedź działała dla mnie dobrze, ale spowodowała
no-param-reassign
błąd ESLint - https://eslint.org/docs/rules/no-param-reassignOto, jak sobie z tym poradziłem, upewniając się, że utworzyłem kopię stanu (co, moim zdaniem, jest rzeczą Reduxy do zrobienia ...):
Ale może utworzenie kopii stanu w celu przekazania jej do innej funkcji reduktora, która tworzy kopię, jest nieco zbyt skomplikowane? Nie brzmi to ładnie, ale jest bardziej rzeczowy:
źródło
Oprócz odpowiedzi Dana Abramova nie powinniśmy jawnie ustawiać akcji jako action = {type: '@@ INIT'} obok state = undefined. Przy powyższym typie działania każdy reduktor zwraca stan początkowy.
źródło
na serwerze mam zmienną to: global.isSsr = true a w każdym reduktorze mam const to: initialState Aby zresetować dane w sklepie, wykonuję następujące czynności dla każdego Reducer: przykład z appReducer.js :
wreszcie na routerze serwera:
źródło
Poniższe rozwiązanie działa dla mnie.
Po uruchomieniu naszej aplikacji stan reduktora jest nowy i nowy z domyślną wartością InitialState .
Musimy dodać akcję, która wywołuje początkowe ładowanie aplikacji, aby pozostała w stanie domyślnym .
Podczas logowania się z aplikacji możemy proste przypisanie stan domyślny i reduktor będzie działać tak samo jak nowy .
Główny kontener aplikacji
Główny reduktor aplikacji
Po wylogowaniu akcja wywołująca dla resetowania stanu
Mam nadzieję, że to rozwiąże twój problem!
źródło
Aby zresetować stan do stanu początkowego, napisałem następujący kod:
źródło
Jedną z rzeczy, której rozwiązanie w zaakceptowanej odpowiedzi nie robi, jest wyczyszczenie pamięci podręcznej dla sparametryzowanych selektorów. Jeśli masz taki selektor:
Następnie musisz je zwolnić podczas wylogowywania:
W przeciwnym razie zapamiętana wartość ostatniego wywołania selektora i wartości ostatnich parametrów pozostaną w pamięci.
Próbki kodu pochodzą z dokumentów ngrx .
źródło
dla mnie najlepsze było ustawienie
initialState
zamiaststate
:źródło
Inną opcją jest:
'@@redux/INIT'
jest typem akcji, która redux jest wysyłana automatycznie, kiedy tycreateStore
, więc zakładając, że wszystkie reduktory mają już wartość domyślną, zostaną złapane przez te i zaczną od nowa swój stan. Można to jednak uznać za prywatny szczegół implementacji redux, więc kupujący uważaj ...źródło
Po prostu wyczyść sesję linku do wylogowania i odśwież stronę. Twój sklep nie wymaga dodatkowego kodu. Za każdym razem, gdy chcesz całkowicie zresetować stan, odświeżenie strony jest prostym i łatwo powtarzalnym sposobem obsługi tego.
źródło
źródło
window.location.reload();
window
, np. Reakcyjnym rodzimym