Wie setze ich den Status eines Redux-Speichers zurück?


455

Ich benutze Redux für das State Management.
Wie setze ich den Speicher in den Ausgangszustand zurück?

Angenommen, ich habe zwei Benutzerkonten ( u1und u2).
Stellen Sie sich die folgende Abfolge von Ereignissen vor:

  1. Der Benutzer u1meldet sich bei der App an und unternimmt etwas, sodass wir einige Daten im Store zwischenspeichern.

  2. Benutzer u1meldet sich ab.

  3. Der Benutzer u2meldet sich bei der App an, ohne den Browser zu aktualisieren.

Zu diesem Zeitpunkt werden die zwischengespeicherten Daten verknüpft u1, und ich möchte sie bereinigen.

Wie kann ich den Redux-Speicher auf den Ausgangszustand zurücksetzen, wenn sich der erste Benutzer abmeldet?


8
Es ist wahrscheinlich besser, den Status beim Abmelden zu löschen (aus Sicherheitsgründen)
Clarkie

Antworten:


1033

Eine Möglichkeit, dies zu tun, besteht darin, einen Root-Reduzierer in Ihre Anwendung zu schreiben.

Der Root-Reduzierer delegiert normalerweise die Verarbeitung der Aktion an den Reduzierer, der von generiert wird combineReducers(). Wenn jedoch eine USER_LOGOUTAktion empfangen wird , wird der Ausgangszustand erneut zurückgegeben.

Wenn Ihr Root-Reducer beispielsweise so aussah:

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

Sie können es umbenennen appReducerund eine neue rootReducerDelegierung darauf schreiben :

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

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

Jetzt müssen wir nur noch das Neue lehren rootReducer, um nach der USER_LOGOUTAktion den Ausgangszustand wiederherzustellen. Wie wir wissen, sollen Reduzierer den Ausgangszustand zurückgeben, wenn sie undefinedals erstes Argument aufgerufen werden , unabhängig von der Aktion. Verwenden statewir diese Tatsache, um das Akkumulierte unter bestimmten Bedingungen zu entfernen appReducer:

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

  return appReducer(state, action)
}

Jetzt werden bei jedem USER_LOGOUTBrand alle Reduzierungen neu initialisiert. Sie können auch etwas anderes zurückgeben als ursprünglich, wenn sie möchten, da sie dies auch überprüfen können action.type.

Um es noch einmal zu wiederholen: Der vollständige neue Code sieht folgendermaßen aus:

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

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

  return appReducer(state, action)
}

Beachten Sie, dass ich den Status hier nicht mutiere, sondern lediglich die Referenz einer aufgerufenen lokalen Variablen neu zuweise, statebevor ich sie an eine andere Funktion übergebe. Das Mutieren eines Zustandsobjekts wäre ein Verstoß gegen die Redux-Prinzipien.

Wenn Sie Redux-Persist verwenden , müssen Sie möglicherweise auch Ihren Speicher reinigen. Redux-persist speichert eine Kopie Ihres Status in einer Speicher-Engine, und die Statuskopie wird bei der Aktualisierung von dort geladen.

Zuerst müssen Sie die entsprechende Speicher-Engine importieren und dann den Status analysieren, bevor Sie ihn festlegen undefinedund jeden Speicherstatus-Schlüssel bereinigen.

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
Ich bin neugierig, Dan, könntest du so etwas auch in deinem Reduzierer machen? mit CLEAR_DATA als Aktion. case 'CLEAR_DATA': return initialState
HussienK

6
@HussienK das würde funktionieren aber nicht auf den Zustand für jeden Reduzierer.
Cory Danielson

12
Hier ist eine Version, in der Sie die Reduzierer dynamisch kombinieren, falls Sie asynchrone Reduzierer verwenden: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
Klärt dieser Ansatz den Staat und seine gesamte Geschichte vollständig? Ich denke aus Sicherheitsgründen: Wenn dies implementiert wurde USER_LOGOUT, ist es nach dem Auslösen der Aktion möglich, Statusdaten von früher zu erhalten? (zB über Devtools)
AlexKempton

81

Ich möchte darauf hinweisen, dass der akzeptierte Kommentar von Dan Abramov korrekt ist, außer dass bei der Verwendung des React-Router-Redux-Pakets zusammen mit diesem Ansatz ein seltsames Problem aufgetreten ist. Unser Fix bestand darin, den Status nicht auf undefinedden aktuellen Routing-Reduzierer zu setzen, sondern diesen weiterhin zu verwenden. Daher würde ich vorschlagen, die folgende Lösung zu implementieren, wenn Sie dieses Paket verwenden

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

19
Ich denke, das Wichtigste dabei ist, dass Sie möglicherweise nicht den gesamten Statusbaum beim Abmelden löschen möchten - der Ansatz funktioniert beim Root-Reduzierer eines Teilbaums gleich gut, und daher ist es möglicherweise klarer, diese Technik nur bei den Root-Reduzierern des Teilbaums anzuwenden Teilbaum (n) Sie tun löschen wollen, anstatt Kommissionierung ‚besondere‘ Kinder nicht klar an der Wurzel Minderer des gesamten Baumes, wie diese
davnicwil

1
Ich glaube, ich habe dieses Problem, auf das Sie sich gerade beziehen (wo beim Abmelden der Router auf den richtigen Pfad gesetzt wird, aber eine völlig andere Komponente geladen wird). Ich habe etwas Ähnliches implementiert, um es zu beheben, aber ich denke etwas mit unveränderlich js massiert dies. Am Ende habe ich einen übergeordneten Reduzierer erstellt, der die Aktion RESET-STATE hat, und ich erbe von diesem Reduzierer, um zu vermeiden, dass das Routing insgesamt berührt wird
Neta Meta

Hatte ähnliche Probleme, dies hat es behoben. Vielen Dank.
Lloyd Watkin

3
Beachten Sie, dass mit routerrounting
React

2
@ Mrchief es hängt davon ab, wie Sie es in Ihrem definiert haben combineReducers()..... wenn Sie combineReducers({routing: routingReducer})es hätten, wäre es wie in der Antwort beschrieben
Ben Lonsdale

40

Aktion definieren:

const RESET_ACTION = {
  type: "RESET"
}

Dann in jedem Ihrer Reduzierer unter der Annahme, dass Sie mehrere Aktionen durch jeden Reduzierer verwenden switchoder if-elseausführen. Ich werde den Fall für eine nehmen 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; 
  }
}

Auf diese Weise RESETaktualisiert Ihr Reduzierer den Speicher bei jedem Aufruf von action mit dem Standardstatus.

Zum Abmelden können Sie jetzt Folgendes tun:

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

Jedes Mal, wenn sich ein Benutzer anmeldet, ohne dass ein Browser aktualisiert wird. Der Speicher ist immer standardmäßig aktiviert.

store.dispatch(RESET_ACTION)arbeitet nur die Idee aus. Sie werden höchstwahrscheinlich einen Aktionsersteller für diesen Zweck haben. Ein viel besserer Weg wird sein, dass Sie eine haben LOGOUT_ACTION.

Sobald Sie dies versenden LOGOUT_ACTION. Eine benutzerdefinierte Middleware kann diese Aktion dann entweder mit Redux-Saga oder Redux-Thunk abfangen. In beiden Fällen können Sie jedoch eine andere Aktion 'RESET' auslösen. Auf diese Weise erfolgt das Abmelden und Zurücksetzen des Geschäfts synchron und Ihr Geschäft ist für die Anmeldung eines anderen Benutzers bereit.


1
Ich denke, dies ist der bessere Ansatz, als nur den Status so einzustellen, undefineddass er in der anderen Antwort gefällt. Wenn Ihre Anwendung einen Statusbaum erwartet und Sie ihn undefinedstattdessen angeben, müssen nur mehr Fehler und Kopfschmerzen behoben werden als nur ein leerer Baum.
Worc

3
@worc Der Zustand wird nicht wirklich undefiniert sein, da Reduzierer initialState zurückgeben, wenn sie einen undefinierten Zustand erhalten
Guillaume

3
@worc denken, dass Sie bei diesem Ansatz jedes Mal, wenn jemand einen neuen Reduzierer erstellt, daran denken müssen, den Rücksetzfall hinzuzufügen.
Francute

1
Ich habe definitiv meine Meinung geändert, aus diesen beiden Gründen und der Idee, dass eine RESET_ACTION eine Aktion ist . es gehört also zunächst nicht wirklich in den Reduzierer.
worc

1
Dies ist definitiv der richtige Ansatz. Das Setzen des Zustands auf etwas anderes als den Anfangszustand bittet nur um Ärger
Sebastian Serrano

15

Nur eine vereinfachte Antwort auf die beste Antwort:

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


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

Das funktioniert, danke. Ich war von Dans Antwort, aber ich kann es nicht herausfinden.
Aljohn Yamaro

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

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

   return state
 }

Sie können auch eine Aktion auslösen, die von allen oder einigen Reduzierern ausgeführt wird und die Sie auf den ursprünglichen Speicher zurücksetzen möchten. Eine Aktion kann einen Reset auf Ihren gesamten Status auslösen oder nur einen Teil davon, der Ihnen passend erscheint. Ich glaube, dies ist der einfachste und kontrollierbarste Weg, dies zu tun.


10

Wenn Sie mit Redux die folgende Lösung angewendet haben, wird davon ausgegangen, dass ich in allen meinen Reduzierungen einen initialState festgelegt habe (z. B. {user: {name, email}}). In vielen Komponenten überprüfe ich diese verschachtelten Eigenschaften, sodass ich mit diesem Fix verhindere, dass meine Rendermethoden unter gekoppelten Eigenschaftsbedingungen beschädigt werden (z. B. wenn state.user.email, das einen Fehler auslöst, undefiniert ist, wenn oben genannte Lösungen verwendet werden).

const appReducer = combineReducers({
  tabs,
  user
})

const initialState = appReducer({}, {})

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

  return appReducer(state, action)
}

7

UPDATE NGRX4

Wenn Sie auf NGRX 4 migrieren, haben Sie möglicherweise aus dem Migrationshandbuch festgestellt , dass die Rootreducer-Methode zum Kombinieren Ihrer Reduzierer durch die ActionReducerMap-Methode ersetzt wurde. Diese neue Vorgehensweise könnte das Zurücksetzen des Status zunächst zu einer Herausforderung machen. Es ist eigentlich unkompliziert, aber die Art und Weise, dies zu tun, hat sich geändert.

Diese Lösung ist vom Meta- Reducer -API-Abschnitt der NGRX4 Github-Dokumente inspiriert .

Nehmen wir zunächst an, Sie kombinieren Ihre Reduzierungen wie folgt mit der neuen ActionReducerMap-Option von 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
}

Angenommen, Sie möchten den Status in app.module `zurücksetzen

//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 { }

`

Und das ist im Grunde eine Möglichkeit, mit NGRX 4 den gleichen Effekt zu erzielen.


5

routerIch kombinierte die Ansätze von Dan, Ryan und Rob, um den Zustand beizubehalten und alles andere im Staatsbaum zu initialisieren, und kam zu folgendem Ergebnis:

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

4

Ich habe eine Komponente erstellt, mit der Redux den Status zurücksetzen kann. Sie müssen diese Komponente nur verwenden, um Ihr Geschäft zu erweitern und einen bestimmten action.type auszulösen, um das Zurücksetzen auszulösen. Der Gedanke an die Implementierung ist der gleiche wie der von @Dan Abramov.

Github: https://github.com/wwayne/redux-reset


4

Ich habe Aktionen erstellt, um den Status zu löschen. Wenn ich also einen Ersteller einer Abmeldeaktion versende, versende ich auch Aktionen, um den Status zu löschen.

Benutzerdatensatzaktion

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

Abmeldeaktionsersteller

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

Reduzierstück

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
  }
};

Ich bin mir nicht sicher, ob dies optimal ist?


2

Wenn Sie Redux-Aktionen verwenden , finden Sie hier eine schnelle Problemumgehung mit einem HOF ( Higher Order Function ) für handleActions.

import { handleActions } from 'redux-actions';

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

Und dann handleActionsExanstelle von Original verwenden handleActions, um Reduzierstücke zu handhaben.

Dans Antwort gibt eine großartige Vorstellung von diesem Problem, aber es hat für mich nicht gut geklappt, weil ich es benutze redux-persist.
Bei Verwendung mit redux-persistlöste das einfache Übergeben des undefinedStatus kein anhaltendes Verhalten aus, sodass ich wusste, dass ich das Element manuell aus dem Speicher entfernen musste (in meinem Fall also React Native AsyncStorage).

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

oder

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

hat auch bei mir nicht funktioniert - sie haben mich nur angeschrien. (zB Beschwerden wie "Unerwarteter Schlüssel _persist ..." )

Dann dachte ich plötzlich darüber nach, dass ich nur möchte, dass jeder einzelne Reduzierer seinen eigenen Ausgangszustand zurückgibt, wenn ein RESETAktionstyp auftritt. Auf diese Weise wird das Fortbestehen auf natürliche Weise gehandhabt. Ohne die oben genannte Utility-Funktion ( handleActionsEx) sieht mein Code natürlich nicht trocken aus (obwohl es nur ein RESET: () => initialStateEinzeiler ist , dh ), aber ich konnte es nicht ertragen, weil ich die Metaprogrammierung liebe.


2

Die folgende Lösung hat bei mir funktioniert.

Ich habe Meta-Reduzierern die Funktion zum Zurücksetzen des Status hinzugefügt. Der Schlüssel war zu verwenden

return reducer(undefined, action);

um alle Reduzierstücke in den Ausgangszustand zu versetzen. Die Rückgabe undefinedverursachte Fehler, da die Struktur des Geschäfts zerstört wurde.

/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

Aus Sicht der Sicherheit zu tun , die sicherste Sache , wenn ein Benutzer aus der Anmeldung ist es, alle persistenten Zustand (ex Cookies, zurückgesetzt localStorage, IndexedDB, Web SQLusw.) und eine harte Aktualisierung der Seite mit tun window.location.reload(). Es ist möglich, dass ein schlampiger Entwickler versehentlich oder absichtlich vertrauliche Daten windowim DOM usw. gespeichert hat. Das Wegblasen des gesamten dauerhaften Status und das Aktualisieren des Browsers ist die einzige Möglichkeit, um sicherzustellen, dass keine Informationen des vorherigen Benutzers an den nächsten Benutzer weitergegeben werden.

(Natürlich sollten Sie als Benutzer auf einem gemeinsam genutzten Computer den Modus "Privates Surfen" verwenden, das Browserfenster selbst schließen, die Funktion "Browserdaten löschen" usw. verwenden, aber als Entwickler können wir nicht erwarten, dass dies immer jeder ist so fleißig)


1
Warum haben die Leute das abgelehnt? Wenn Sie einen neuen Redux-Status mit leerem Inhalt ausführen, haben Sie im Grunde immer noch die vorherigen Status im Speicher und können theoretisch von dort aus auf die Daten zugreifen. Das Aktualisieren des Browsers ist Ihre sicherste Wahl!
Wilhelm Sorban

2

Meine Problemumgehung bei der Arbeit mit Typoskript basiert auf Dans Antwort (Redux-Typisierungen machen es unmöglich, undefinedals erstes Argument an Reducer zu übergeben, daher speichere ich den anfänglichen Root-Status in einer Konstanten zwischen).

// 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

Die akzeptierte Antwort half mir, meinen Fall zu lösen. Ich stieß jedoch auf einen Fall, in dem nicht der gesamte Staat geklärt werden musste. Also - ich habe es so gemacht:

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;

Hoffe das hilft jemand anderem :)


Was ist, wenn ich mehr als 10 Zustände habe, aber den Zustand nur eines Reduzierers zurücksetzen möchte?
Paul

1

Nur eine Erweiterung der Antwort von @ dan-abramov . Manchmal müssen wir möglicherweise verhindern, dass bestimmte Schlüssel zurückgesetzt werden.

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

Eine schnelle und einfache Option, die für mich funktioniert hat, war die Verwendung von Redux-Reset . Das war unkompliziert und hat auch einige erweiterte Optionen für größere Apps.

Setup im Create Store

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)

Versenden Sie Ihren 'Reset' in Ihrer Abmeldefunktion

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

Hoffe das hilft


1

Ich gehe davon aus, dass Redux nicht auf dieselbe Variable des Ausgangszustands verweist:

// 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;
  }
};

Die Idee, den Standardstatus als Funktion zu schreiben, hat hier wirklich den Tag gerettet. Vielen Dank
Chris

0

Dieser Ansatz ist sehr richtig: Zerstören Sie einen bestimmten Status "NAME", um andere zu ignorieren und zu behalten.

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

Wenn Sie nur einen Teil Ihres Statusbaums zurücksetzen müssen, können Sie auch USER_LOGOUTin diesem Reduzierer nachsehen und dort damit umgehen.
Andy_D

0

warum benutzt du nicht einfach 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;
}

Hinweis: Stellen Sie sicher, dass Sie den Standardwert für die Aktion auf " {}OK" setzen, da Sie beim Überprüfen action.typeder switch-Anweisung keinen Fehler feststellen möchten .


0

Ich fand, dass die akzeptierte Antwort für mich gut funktionierte, aber den ESLint- no-param-reassignFehler auslöste - https://eslint.org/docs/rules/no-param-reassign

So habe ich es stattdessen gehandhabt und sichergestellt, dass eine Kopie des Status erstellt wird (was meines Wissens die Reduxy-Aufgabe ist ...):

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

Aber vielleicht ist das Erstellen einer Kopie des Status, um ihn einfach an eine andere Reduzierungsfunktion zu übergeben, die eine Kopie davon erstellt, etwas zu kompliziert? Das liest sich nicht so gut, ist aber mehr auf den Punkt:

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

0

Sollten wir zusätzlich zu Dan Abramovs Antwort nicht explizit action = action = 'type:' @@ INIT '} neben state = undefined setzen. Mit dem obigen Aktionstyp gibt jeder Reduzierer den Anfangszustand zurück.


0

Auf dem Server habe ich eine Variable: global.isSsr = true und in jedem Reduzierer habe ich eine Konstante : initialState Um die Daten im Store zurückzusetzen, gehe ich mit jedem Reduzierer wie folgt vor: Beispiel mit 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;
        }
    }
}

Endlich auf dem Router des Servers:

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

0

Die folgende Lösung funktioniert für mich.

Zum ersten Mal auf der Einleitung unserer Anwendung ist der Reduzierer Zustand frisch und neu mit Standardinitial .

Wir müssen eine Aktion hinzufügen, die die APP-Anfangslast aufruft, um den Standardstatus beizubehalten .

Während Sie sich von der Anwendung abmelden, können Sie den Standardstatus einfach neu zuweisen, und der Reduzierer funktioniert genauso neu .

Haupt-APP-Container

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

Haupt-APP-Reduzierer

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);
};

Beim Abmelden wird eine Aktion zum Zurücksetzen des Status aufgerufen

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
    });
  }
}

Hoffe das löst dein Problem!


0

Damit ich den Status auf den ursprünglichen Status zurücksetzen kann, habe ich den folgenden Code geschrieben:

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

0

Eine Sache, die die Lösung in der akzeptierten Antwort nicht bewirkt, ist das Löschen des Caches für parametrisierte Selektoren. Wenn Sie einen Selektor wie diesen haben:

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

Dann müssten Sie sie beim Abmelden folgendermaßen freigeben:

selectTotal.release();

Andernfalls werden der gespeicherte Wert für den letzten Aufruf des Selektors und die Werte der letzten Parameter weiterhin gespeichert.

Codebeispiele stammen aus den ngrx-Dokumenten .


0

Für mich hat es am besten funktioniert, das initialStateanstelle von state:

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

-1

Eine andere Option ist:

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

'@@redux/INIT'ist der Aktionstyp, den Redux automatisch versendet, wenn Sie createStorealso davon ausgehen, dass Ihre Reduzierer bereits einen Standard haben. Dies würde von diesen abgefangen werden und Ihren Status neu starten. Es könnte jedoch als privates Implementierungsdetail von Redux angesehen werden, also Vorsicht beim Käufer ...


Ich habe getan, dass es den Status nicht ändert, außerdem habe ich @@ INIT ausprobiert, was in ReduxDevtools als erste Aktion angezeigt wird
Reza

-2

Lassen Sie einfach Ihren Abmeldelink die Sitzung löschen und aktualisieren Sie die Seite. Für Ihr Geschäft wird kein zusätzlicher Code benötigt. Jedes Mal, wenn Sie den Status vollständig zurücksetzen möchten, ist eine Seitenaktualisierung eine einfache und leicht wiederholbare Methode, um damit umzugehen.


1
Was ist, wenn Sie eine Middleware verwenden, die den Speicher mit localstorage synchronisiert? Dann funktioniert Ihr Ansatz überhaupt nicht ...
Spock

8
Ich verstehe nicht wirklich, warum Leute solche Antworten ablehnen.
Wylliam Judd

Warum haben die Leute das abgelehnt? Wenn Sie einen neuen Redux-Status mit leerem Inhalt ausführen, haben Sie im Grunde immer noch die vorherigen Status im Speicher und können theoretisch von dort aus auf die Daten zugreifen. Das Aktualisieren des Browsers ist Ihre sicherste Wahl!
Wilhelm Sorban

-3

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


erklären? @ SinanSamet
Nikita Beznosikov

Um klar zu sein, habe ich dies nicht abgelehnt. Aber ich entmutige es. Wenn Sie zum Anmelden und erneuten Laden navigieren, werden Sie sich wahrscheinlich mit Ja abmelden. Aber nur, weil du deinen Staat verloren hast. Wenn Sie nicht redux-persist verwenden, werden Sie sich in diesem Fall nicht einmal abmelden. Insgesamt hasse ich es einfach, Funktionen wiewindow.location.reload();
Sinan Samet

- Schreiben Sie keine solchen Funktionen. - Warum? - Ich mag es nicht.
Nikita Beznosikov

1
Das Neuladen der Seite unterscheidet sich vom Zurücksetzen des Speicherstatus. Außerdem könnten Sie sich in einer Umgebung befinden window, in der z. B. nicht reaktionsfreudig ist
Max
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.