Jak zresetować stan sklepu Redux?


455

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 ( u1i u2).
Wyobraź sobie następującą sekwencję zdarzeń:

  1. Użytkownik u1loguje się do aplikacji i coś robi, więc buforujemy niektóre dane w sklepie.

  2. Użytkownik u1wylogowuje się.

  3. Użytkownik u2loguje się do aplikacji bez odświeżania przeglądarki.

W tym momencie buforowane dane zostaną skojarzone u1i chciałbym je wyczyścić.

Jak mogę zresetować magazyn Redux do stanu początkowego po wylogowaniu pierwszego użytkownika?


8
Prawdopodobnie lepiej jest wyczyścić stan po wylogowaniu (z punktu widzenia bezpieczeństwa)
Clarkie

Odpowiedzi:


1033

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 odbiera USER_LOGOUTakcję, przywraca stan początkowy od nowa.

Na przykład, jeśli Twój reduktor root wyglądał tak:

const rootReducer = combineReducers({
  /* your app’s top-level reducers */
})

Możesz zmienić nazwę na appReduceri napisać nowego rootReducerdelegata:

const appReducer = combineReducers({
  /* your app’s top-level reducers */
})

const rootReducer = (state, action) => {
  return appReducer(state, action)
}

Teraz musimy tylko nauczyć nowego rootReducerpowrotu do stanu początkowego po USER_LOGOUTakcji. Jak wiemy, reduktory powinny zwracać stan początkowy, gdy są wywoływane undefinedjako pierwszy argument, bez względu na akcję. Wykorzystajmy ten fakt do warunkowego usunięcia nagromadzonego materiału, stategdy przekazujemy go do appReducer:

 const rootReducer = (state, action) => {
  if (action.type === 'USER_LOGOUT') {
    state = undefined
  }

  return appReducer(state, action)
}

Teraz przy każdym USER_LOGOUTuruchomieniu 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:

const appReducer = combineReducers({
  /* your app’s top-level reducers */
})

const rootReducer = (state, action) => {
  if (action.type === 'USER_LOGOUT') {
    state = undefined
  }

  return appReducer(state, action)
}

Zauważ, że nie mutuję tutaj stanu, po prostu ponownie przypisuję odwołanie do zmiennej lokalnej wywołanej stateprzed 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 undefinedi wyczyścić każdy klucz stanu pamięci masowej.

const rootReducer = (state, action) => {
    if (action.type === SIGNOUT_REQUEST) {
        // for all keys defined in your persistConfig(s)
        storage.removeItem('persist:root')
        // storage.removeItem('persist:otherKey')

        state = undefined;
    }
    return appReducer(state, action);
};

15
Jestem ciekawy, Dan, czy mógłbyś zrobić coś takiego w reduktorze. z CLEAR_DATA będącą akcją. case 'CLEAR_DATA': return initialState
HussienK

6
@HussienK, który działałby, ale nie w stanie dla każdego reduktora.
Cory Danielson

12
Oto wersja, w której dynamicznie łączysz reduktory w przypadku korzystania z reduktorów asynchronicznych:export const createRootReducer = asyncReducers => { const appReducer = combineReducers({ myReducer ...asyncReducers }); return (state, action) => { if (action.type === 'LOGOUT_USER') { state = undefined; } return appReducer(state, action); } };
Ivo Sabev

2
if (action.type === 'RESET') return action.stateFromLocalStorage
Dan Abramov,

3
Czy takie podejście całkowicie usuwa stan i całą jego historię? Myślę z perspektywy bezpieczeństwa: jeśli zostało to zaimplementowane, to po uruchomieniu USER_LOGOUTakcji można wcześniej uzyskać dane o stanie? (np. przez devtools)
AlexKempton,

81

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, undefinedale raczej używać obecnego reduktora routingu. Sugeruję więc wdrożenie poniższego rozwiązania, jeśli korzystasz z tego pakietu

const rootReducer = (state, action) => {
  if (action.type === 'USER_LOGOUT') {
    const { routing } = state
    state = { routing } 
  }
  return appReducer(state, action)
}

19
Myślę, że na wynos jest to, że możesz nie chcieć wyczyścić całego drzewa stanu po wylogowaniu - podejście działa równie dobrze na reduktorze korzeni dowolnego poddrzewa, więc może być łatwiej zastosować tę technikę tylko na reduktorach korzenia poddrzewa, które chcesz wyczyścić, zamiast wybierania „specjalnych” dzieci, które nie powinny być
usuwane

1
Wydaje mi się, że mam problemy, o których mówisz teraz (gdy podczas wylogowywania ustawi trasę na właściwą ścieżkę, ale załaduje się kompletny inny komponent), zaimplementowałem coś podobnego do twojego, aby to naprawić, ale myślę, że coś z niezmienny js masuje to. W końcu stworzyłem nadrzędny reduktor, który ma działanie RESET-STATE, i dziedziczę po tym reduktorze, aby całkowicie uniknąć dotykania routingu
Neta Meta

Wystąpiły podobne problemy, to rozwiązało. Dzięki.
Lloyd Watkin

3
Zauważ, że w przypadku routera-reakcji-redux właściwość jest, routera NIErounting
Mrchief

2
@ Przesłanie zależy od tego, jak to zdefiniowałeś w swoim combineReducers()..... gdybyś to zrobił combineReducers({routing: routingReducer}), tak jak opisano w odpowiedzi
Ben Lonsdale

39

Zdefiniuj akcję:

const RESET_ACTION = {
  type: "RESET"
}

Następnie w każdym ze swoich reduktorów, zakładając, że używasz switchlub if-elsedo obsługi wielu działań przez każdy reduktor. Zajmę się sprawą switch.

const INITIAL_STATE = {
  loggedIn: true
}

const randomReducer = (state=INITIAL_STATE, action) {
  switch(action.type) {
    case 'SOME_ACTION_TYPE':

       //do something with it

    case "RESET":

      return INITIAL_STATE; //Always return the initial state

   default: 
      return state; 
  }
}

W ten sposób za każdym razem, gdy wywołasz RESETakcję, reduktor zaktualizuje sklep do stanu domyślnego.

Teraz, aby się wylogować, możesz wykonać następujące czynności:

const logoutHandler = () => {
    store.dispatch(RESET_ACTION)
    // Also the custom logic like for the rest of the logout handler
}

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 posiadanie LOGOUT_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.


1
Wydaje mi się, że jest to lepsze podejście niż ustawienie stanu undefinedtak, jak w drugiej odpowiedzi. gdy twoja aplikacja oczekuje drzewa stanu, a undefinedzamiast tego podajesz , jest więcej błędów i problemów niż tylko puste drzewo.
worc

3
@worc Stan nie będzie tak naprawdę niezdefiniowany, ponieważ reduktory zwracają stan początkowy, gdy otrzymają niezdefiniowany stan
Guillaume

3
@worc uważa, że ​​przy takim podejściu za każdym razem, gdy ktoś utworzy nowy reduktor, trzeba będzie pamiętać o dodaniu skrzynki resetowania.
Francute,

1
Zdecydowanie zmieniłem zdanie na ten temat, z obu tych powodów, plus pomysł, że RESET_ACTION to akcja . więc tak naprawdę nie należy do reduktora.
worc,

1
To zdecydowanie właściwe podejście. Ustawienie stanu na cokolwiek innego niż stan początkowy wymaga jedynie kłopotów
Sebastian Serrano

15

Uproszczona odpowiedź na najlepszą odpowiedź:

const rootReducer = combineReducers({
    auth: authReducer,
    ...formReducers,
    routing
});


export default (state, action) =>
  rootReducer(action.type === 'USER_LOGOUT' ? undefined : state, action);

to działa, dziękuję, byłem z odpowiedzi Dana, ale nie mogę tego rozgryźć.
Aljohn Yamaro

14
 const reducer = (state = initialState, { type, payload }) => {

   switch (type) {
      case RESET_STORE: {
        state = initialState
      }
        break
   }

   return state
 }

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.


10

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).

const appReducer = combineReducers({
  tabs,
  user
})

const initialState = appReducer({}, {})

const rootReducer = (state, action) => {
  if (action.type === 'LOG_OUT') {
    state = initialState
  }

  return appReducer(state, action)
}

7

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:

//index.reducer.ts
export const reducers: ActionReducerMap<State> = {
    auth: fromAuth.reducer,
    layout: fromLayout.reducer,
    users: fromUsers.reducer,
    networks: fromNetworks.reducer,
    routingDisplay: fromRoutingDisplay.reducer,
    routing: fromRouting.reducer,
    routes: fromRoutes.reducer,
    routesFilter: fromRoutesFilter.reducer,
    params: fromParams.reducer
}

Powiedzmy, że chcesz zresetować stan z poziomu app.module `

//app.module.ts
import { IndexReducer } from './index.reducer';
import { StoreModule, ActionReducer, MetaReducer } from '@ngrx/store';
...
export function debug(reducer: ActionReducer<any>): ActionReducer<any> {
    return function(state, action) {

      switch (action.type) {
          case fromAuth.LOGOUT:
            console.log("logout action");
            state = undefined;
      }

      return reducer(state, action);
    }
  }

  export const metaReducers: MetaReducer<any>[] = [debug];

  @NgModule({
    imports: [
        ...
        StoreModule.forRoot(reducers, { metaReducers}),
        ...
    ]
})

export class AppModule { }

`

I to jest w zasadzie jeden sposób na osiągnięcie tego samego efektu za pomocą NGRX 4.


5

Łącząc podejście Dana, Ryana i Roba, aby uwzględnić utrzymanie routerstanu i zainicjowanie wszystkiego innego w drzewie stanu, skończyłem na tym:

const rootReducer = (state, action) => appReducer(action.type === LOGOUT ? {
    ...appReducer({}, {}),
    router: state && state.router || {}
  } : state, action);

4

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


4

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

export const clearUserRecord = () => ({
  type: CLEAR_USER_RECORD
});

Kreator akcji wylogowania

export const logoutUser = () => {
  return dispatch => {
    dispatch(requestLogout())
    dispatch(receiveLogout())
    localStorage.removeItem('auth_token')
    dispatch({ type: 'CLEAR_USER_RECORD' })
  }
};

Reduktor

const userRecords = (state = {isFetching: false,
  userRecord: [], message: ''}, action) => {
  switch (action.type) {
    case REQUEST_USER_RECORD:
    return { ...state,
      isFetching: true}
    case RECEIVE_USER_RECORD:
    return { ...state,
      isFetching: false,
      userRecord: action.user_record}
    case USER_RECORD_ERROR:
    return { ...state,
      isFetching: false,
      message: action.message}
    case CLEAR_USER_RECORD:
    return {...state,
      isFetching: false,
      message: '',
      userRecord: []}
    default:
      return state
  }
};

Nie jestem pewien, czy to jest optymalne?


2

Jeśli używasz akcji redux , oto szybkie obejście problemu za pomocą HOF ( funkcja wyższego rzędu ) handleActions.

import { handleActions } from 'redux-actions';

export function handleActionsEx(reducer, initialState) {
  const enhancedReducer = {
    ...reducer,
    RESET: () => initialState
  };
  return handleActions(enhancedReducer, initialState);
}

A następnie użyj handleActionsExzamiast oryginału handleActionsdo 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-persistzwykłym przejściem w undefinedstan nie spowodowało trwałego zachowania, więc wiedziałem, że muszę ręcznie usunąć element z pamięci (w tym przypadku React Native AsyncStorage).

await AsyncStorage.removeItem('persist:root');

lub

await persistor.flush(); // or await persistor.purge();

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 RESETnapotkaniu 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.


2

Poniższe rozwiązanie działało dla mnie.

Dodałem funkcję resetowania stanu do meta reduktorów. Kluczem było użycie

return reducer(undefined, action);

ustawić wszystkie reduktory do stanu początkowego. undefinedZamiast tego powrót spowodował błędy z powodu zniszczenia struktury sklepu.

/reducers/index.ts

export function resetState(reducer: ActionReducer<State>): ActionReducer<State> {
  return function (state: State, action: Action): State {

    switch (action.type) {
      case AuthActionTypes.Logout: {
        return reducer(undefined, action);
      }
      default: {
        return reducer(state, action);
      }
    }
  };
}

export const metaReducers: MetaReducer<State>[] = [ resetState ];

app.module.ts

import { StoreModule } from '@ngrx/store';
import { metaReducers, reducers } from './reducers';

@NgModule({
  imports: [
    StoreModule.forRoot(reducers, { metaReducers })
  ]
})
export class AppModule {}

2

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życiem window.location.reload(). Możliwe jest, że niechlujny programista przypadkowo lub umyślnie przechował niektóre poufne dane windoww 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)


1
Dlaczego ludzie głosowali za tym? Kiedy wykonujesz nowy stan redux, z pustą zawartością, w zasadzie nadal masz w pamięci poprzednie stany i możesz teoretycznie uzyskać z nich dostęp do danych. Odświeżenie przeglądarki jest Twoim najbezpieczniejszym wyborem!
Wilhelm Sorban,

2

Moje obejście podczas pracy ze skryptem maszynowym, zbudowane na podstawie odpowiedzi Dana (typy redux uniemożliwiają przekazanie undefineddo reduktora jako pierwszego argumentu, więc buforuję początkowy stan root w stałej):

// store

export const store: Store<IStoreState> = createStore(
  rootReducer,
  storeEnhacer,
)

export const initialRootState = {
  ...store.getState(),
}

// root reducer

const appReducer = combineReducers<IStoreState>(reducers)

export const rootReducer = (state: IStoreState, action: IAction<any>) => {
  if (action.type === "USER_LOGOUT") {
    return appReducer(initialRootState, action)
  }

  return appReducer(state, action)
}


// auth service

class Auth {
  ...

  logout() {
    store.dispatch({type: "USER_LOGOUT"})
  }
}

2

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:

const combinedReducer = combineReducers({
    // my reducers 
});

const rootReducer = (state, action) => {
    if (action.type === RESET_REDUX_STATE) {
        // clear everything but keep the stuff we want to be preserved ..
        delete state.something;
        delete state.anotherThing;
    }
    return combinedReducer(state, action);
}

export default rootReducer;

Mam nadzieję, że to pomoże komuś innemu :)


co jeśli mam więcej niż 10 stanów, ale chcę zresetować stan tylko jednego reduktora?
Paul

1

Po prostu rozszerzenie odpowiedzi @ dan-abramov , czasem może być konieczne zachowanie niektórych kluczy przed zresetowaniem.

const retainKeys = ['appConfig'];

const rootReducer = (state, action) => {
  if (action.type === 'LOGOUT_USER_SUCCESS' && state) {
    state = !isEmpty(retainKeys) ? pick(state, retainKeys) : undefined;
  }

  return appReducer(state, action);
};

1

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

import reduxReset from 'redux-reset'
...
const enHanceCreateStore = compose(
applyMiddleware(...),
reduxReset()  // Will use 'RESET' as default action.type to trigger reset
)(createStore)
const store = enHanceCreateStore(reducers)

Wyślij swój „reset” w funkcji wylogowania

store.dispatch({
type: 'RESET'
})

Mam nadzieję że to pomoże


1

Moje podejście, aby powstrzymać Redux od odwoływania się do tej samej zmiennej stanu początkowego:

// write the default state as a function
const defaultOptionsState = () => ({
  option1: '',
  option2: 42,
});

const initialState = {
  options: defaultOptionsState() // invoke it in your initial state
};

export default (state = initialState, action) => {

  switch (action.type) {

    case RESET_OPTIONS:
    return {
      ...state,
      options: defaultOptionsState() // invoke the default function to reset this part of the state
    };

    default:
    return state;
  }
};

Pomysł zapisania stanu domyślnego jako funkcji naprawdę uratował tutaj dzień. Dziękuję
Chris

0

To podejście jest bardzo słuszne: zniszczyć dowolny stan „NAME”, aby zignorować i zatrzymać innych.

const rootReducer = (state, action) => {
    if (action.type === 'USER_LOGOUT') {
        state.NAME = undefined
    }
    return appReducer(state, action)
}

Jeśli potrzebujesz zresetować tylko jeden element drzewa stanu, możesz także nasłuchiwać USER_LOGOUTw tym reduktorze i tam go obsłużyć.
Andy_D

0

dlaczego po prostu nie użyjesz return module.exports.default();)

export default (state = {pending: false, error: null}, action = {}) => {
    switch (action.type) {
        case "RESET_POST":
            return module.exports.default();
        case "SEND_POST_PENDING":
            return {...state, pending: true, error: null};
        // ....
    }
    return state;
}

Uwaga: upewnij się, że ustawiłeś domyślną wartość akcji na{} i wszystko jest w porządku, ponieważ nie chcesz napotkać błędu podczas sprawdzania action.typew instrukcji switch.


0

Stwierdziłem, że zaakceptowana odpowiedź działała dla mnie dobrze, ale spowodowała no-param-reassignbłąd ESLint - https://eslint.org/docs/rules/no-param-reassign

Oto, jak sobie z tym poradziłem, upewniając się, że utworzyłem kopię stanu (co, moim zdaniem, jest rzeczą Reduxy do zrobienia ...):

import { combineReducers } from "redux"
import { routerReducer } from "react-router-redux"
import ws from "reducers/ws"
import session from "reducers/session"
import app from "reducers/app"

const appReducer = combineReducers({
    "routing": routerReducer,
    ws,
    session,
    app
})

export default (state, action) => {
    const stateCopy = action.type === "LOGOUT" ? undefined : { ...state }
    return appReducer(stateCopy, action)
}

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:

export default (state, action) => {
    return appReducer(action.type === "LOGOUT" ? undefined : state, action)
}

0

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.


0

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 :

 const initialState = {
    auth: {},
    theme: {},
    sidebar: {},
    lsFanpage: {},
    lsChatApp: {},
    appSelected: {},
};

export default function (state = initialState, action) {
    if (typeof isSsr!=="undefined" && isSsr) { //<== using global.isSsr = true
        state = {...initialState};//<= important "will reset the data every time there is a request from the client to the server"
    }
    switch (action.type) {
        //...other code case here
        default: {
            return state;
        }
    }
}

wreszcie na routerze serwera:

router.get('*', (req, res) => {
        store.dispatch({type:'reset-all-blabla'});//<= unlike any action.type // i use Math.random()
        // code ....render ssr here
});

0

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

  componentDidMount() {   
    this.props.persistReducerState();
  }

Główny reduktor aplikacji

const appReducer = combineReducers({
  user: userStatusReducer,     
  analysis: analysisReducer,
  incentives: incentivesReducer
});

let defaultState = null;
export default (state, action) => {
  switch (action.type) {
    case appActions.ON_APP_LOAD:
      defaultState = defaultState || state;
      break;
    case userLoginActions.USER_LOGOUT:
      state = defaultState;
      return state;
    default:
      break;
  }
  return appReducer(state, action);
};

Po wylogowaniu akcja wywołująca dla resetowania stanu

function* logoutUser(action) {
  try {
    const response = yield call(UserLoginService.logout);
    yield put(LoginActions.logoutSuccess());
  } catch (error) {
    toast.error(error.message, {
      position: toast.POSITION.TOP_RIGHT
    });
  }
}

Mam nadzieję, że to rozwiąże twój problem!


0

Aby zresetować stan do stanu początkowego, napisałem następujący kod:

const appReducers = (state, action) =>
   combineReducers({ reducer1, reducer2, user })(
     action.type === "LOGOUT" ? undefined : state,
     action
);

0

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:

export const selectCounter1 = (state: State) => state.counter1;
export const selectCounter2 = (state: State) => state.counter2;
export const selectTotal = createSelector(
  selectCounter1,
  selectCounter2,
  (counter1, counter2) => counter1 + counter2
);

Następnie musisz je zwolnić podczas wylogowywania:

selectTotal.release();

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 .


0

dla mnie najlepsze było ustawienie initialStatezamiast state:

  const reducer = createReducer(initialState,
  on(proofActions.cleanAdditionalInsuredState, (state, action) => ({
    ...initialState
  })),

-1

Inną opcją jest:

store.dispatch({type: '@@redux/INIT'})

'@@redux/INIT'jest typem akcji, która redux jest wysyłana automatycznie, kiedy ty createStore, 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 ...


Zrobiłem to, że nie zmienia stanu, próbowałem też @@ INIT, który jest pokazany w ReduxDevtools jako pierwsza akcja
Reza

-3

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.


1
Co się stanie, jeśli użyjesz oprogramowania pośredniego, które synchronizuje sklep z magazynem lokalnym? W takim razie twoje podejście w ogóle nie działa ...
Spock

8
Naprawdę nie rozumiem, dlaczego ludzie odrzucają takie odpowiedzi.
Wylliam Judd

Dlaczego ludzie głosowali za tym? Kiedy wykonujesz nowy stan redux, z pustą zawartością, w zasadzie nadal masz w pamięci poprzednie stany i możesz teoretycznie uzyskać z nich dostęp do danych. Odświeżenie przeglądarki jest Twoim najbezpieczniejszym wyborem!
Wilhelm Sorban,

-3

onLogout() {
  this.props.history.push('/login'); // send user to login page
  window.location.reload(); // refresh the page
}


wyjaśnić? @SinanSamet
Nikita Beznosikov

Żeby było jasne, nie głosowałem za tym. Ale odradzam to. Jeśli przejdziesz do logowania i przeładujesz, prawdopodobnie wylogujesz się tak. Ale tylko dlatego, że straciłeś swój stan. Chyba że użyjesz Redux-persist, w którym to przypadku nawet się nie wylogujesz. Ogólnie nienawidzę widzieć takich funkcji jakwindow.location.reload();
Sinan Samet,

- Nie pisz takich funkcji. - Dlaczego? - Nie podoba mi się to.
Nikita Beznosikov

1
Ponowne załadowanie strony różni się od resetowania stanu sklepu. Poza tym możesz znajdować się w środowisku bez window, np. Reakcyjnym rodzimym
Max
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.