Comment réinitialiser l'état d'une boutique Redux?

455

J'utilise Redux pour la gestion de l'état.
Comment réinitialiser le magasin à son état initial?

Par exemple, supposons que j'ai deux comptes d'utilisateurs ( u1et u2).
Imaginez la séquence d'événements suivante:

  1. L'utilisateur se u1connecte à l'application et fait quelque chose, nous mettons donc en cache certaines données dans le magasin.

  2. L'utilisateur se u1déconnecte.

  3. L'utilisateur se u2connecte à l'application sans rafraîchir le navigateur.

À ce stade, les données mises en cache seront associées à u1, et je voudrais les nettoyer.

Comment puis-je réinitialiser le magasin Redux à son état initial lorsque le premier utilisateur se déconnecte?

xyz
la source
8
Il est probablement préférable d'effacer l'état lors de la déconnexion à la place (du point de vue de la sécurité)
Clarkie

Réponses:

1033

Une façon de le faire serait d'écrire un réducteur de racine dans votre application.

Le réducteur racine déléguerait normalement la gestion de l'action au réducteur généré par combineReducers(). Cependant, chaque fois qu'il reçoit une USER_LOGOUTaction, il renvoie à nouveau l'état initial.

Par exemple, si votre réducteur de racine ressemblait à ceci:

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

Vous pouvez le renommer appReduceret lui écrire une nouvelle rootReducerdélégation:

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

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

Maintenant, nous avons juste besoin d'apprendre au nouveau rootReducerà retourner l'état initial après l' USER_LOGOUTaction. Comme nous le savons, les réducteurs sont censés retourner l'état initial lorsqu'ils sont appelés avec undefinedcomme premier argument, quelle que soit l'action. Utilisons ce fait pour dépouiller conditionnellement l'accumulé statelorsque nous le transmettons à appReducer:

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

  return appReducer(state, action)
}

Désormais, chaque fois qu'un USER_LOGOUTincendie se déclenche, tous les réducteurs sont réinitialisés. Ils peuvent également retourner quelque chose de différent de ce qu'ils ont fait initialement s'ils le souhaitent, car ils peuvent également vérifier action.type.

Pour réitérer, le nouveau code complet ressemble à ceci:

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

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

  return appReducer(state, action)
}

Notez que je ne mute pas l'état ici, je réaffecte simplement la référence d'une variable locale appelée stateavant de la passer à une autre fonction. La mutation d'un objet d'état constituerait une violation des principes de Redux.

Dans le cas où vous utilisez redux-persist , vous devrez peut-être également nettoyer votre stockage. Redux-persist conserve une copie de votre état dans un moteur de stockage et la copie d'état sera chargée à partir de là lors de l'actualisation.

Vous devez d'abord importer le moteur de stockage approprié , puis analyser l'état avant de le définir undefinedet de nettoyer chaque clé d'état de stockage.

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);
};
Dan Abramov
la source
15
Je suis curieux Dan, pourriez-vous aussi faire quelque chose comme ça dans votre réducteur. avec CLEAR_DATA étant l'action. case 'CLEAR_DATA': return initialState
HussienK
6
@HussienK qui fonctionnerait mais pas sur l'état pour chaque réducteur.
Cory Danielson
12
Voici une version où vous combinez dynamiquement les réducteurs au cas où vous utilisez des réducteurs asynchrones: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
Cette approche efface-t-elle complètement l'État et toute son histoire? Je pense du point de vue de la sécurité: si cela a été mis en œuvre, une fois l' USER_LOGOUTaction déclenchée, est-il possible d'obtenir des données d'état plus tôt? (par exemple via devtools)
AlexKempton
81

Je voudrais souligner que le commentaire accepté par Dan Abramov est correct, sauf que nous avons rencontré un problème étrange lors de l'utilisation du package react-router-redux avec cette approche. Notre correctif consistait à ne pas définir l'état undefinedmais plutôt à utiliser le réducteur de routage actuel. Je suggère donc de mettre en œuvre la solution ci-dessous si vous utilisez ce package

const rootReducer = (state, action) => {
  if (action.type === 'USER_LOGOUT') {
    const { routing } = state
    state = { routing } 
  }
  return appReducer(state, action)
}
Ryan Irilli
la source
19
Je pense que le point à retenir ici est que vous ne voudrez peut-être pas effacer tout l'arbre d'état à la déconnexion - l'approche fonctionne aussi bien au niveau du réducteur de racine de n'importe quel sous-arbre, et il peut donc être plus clair d'appliquer cette technique uniquement aux réducteurs de racine du sous - arbre (s) que vous ne voulez effacer, plutôt que de choisir «spécial enfants à ne pas clairement au réducteur de racine de l'arbre entier, comme celui - ci
davnicwil
1
Je pense que je rencontre ce problème auquel vous faites référence en ce moment (où à la déconnexion, il mettra la route sur le bon chemin mais un composant complètement différent se chargerait) j'ai implémenté quelque chose de similaire au vôtre pour le résoudre, mais je pense que quelque chose avec immuable js masse cela. J'ai fini par créer un réducteur parent qui a une action RESET-STATE, et j'hérite de ce réducteur pour éviter de toucher complètement le routage
Neta Meta
Je rencontrais des problèmes similaires, cela l'a corrigé. Merci.
Lloyd Watkin du
3
Notez qu'avec react-redux-router, la propriété est routeret NONrounting
Mrchief
2
@Mrchief cela dépend de ce que vous avez défini comme dans votre combineReducers()..... si vous l'aviez, combineReducers({routing: routingReducer})ce serait comme décrit dans la réponse
Ben Lonsdale
40

Définissez une action:

const RESET_ACTION = {
  type: "RESET"
}

Ensuite, dans chacun de vos réducteurs, en supposant que vous utilisez switchou if-elsepour gérer plusieurs actions via chaque réducteur. Je vais prendre l'affaire pour un 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; 
  }
}

De cette façon, chaque fois que vous appelez une RESETaction, votre réducteur mettra à jour le magasin avec l'état par défaut.

Maintenant, pour vous déconnecter, vous pouvez gérer ce qui suit:

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

Chaque fois qu'un utilisateur se connecte, sans actualisation du navigateur. Le magasin sera toujours par défaut.

store.dispatch(RESET_ACTION)élabore juste l'idée. Vous aurez très probablement un créateur d'action à cet effet. Une bien meilleure façon sera que vous en ayez un LOGOUT_ACTION.

Une fois que vous expédiez cela LOGOUT_ACTION. Un middleware personnalisé peut alors intercepter cette action, soit avec Redux-Saga ou Redux-Thunk. Cependant, dans les deux cas, vous pouvez envoyer une autre action «RESET». De cette façon, la déconnexion et la réinitialisation du magasin se feront de manière synchrone et votre magasin sera prêt pour une autre connexion utilisateur.

nirbhaygp
la source
1
Je pense que c'est la meilleure approche que de simplement définir l'état undefinedcomme dans l'autre réponse. lorsque votre application attend un arbre d'état et que vous le lui donnez à la undefinedplace, il y a juste plus de bugs et de maux de tête à gérer qu'un simple arbre vide.
worc
3
@worc L'état ne sera en fait pas indéfini, car les réducteurs retournent initialState lorsqu'ils reçoivent un état indéfini
Guillaume
3
@worc pense qu'avec cette approche, chaque fois que quelqu'un crée un nouveau réducteur, vous devez vous rappeler d'ajouter le cas de réinitialisation.
Francute
1
J'ai définitivement changé d'avis à ce sujet, pour ces deux raisons, ainsi que l'idée qu'une RESET_ACTION est une action . donc ça ne fait pas vraiment partie du réducteur pour commencer.
worc
1
C'est certainement la bonne approche. Régler l'état sur autre chose que l'état initial demande simplement des ennuis
Sebastian Serrano
15

Juste une réponse simplifiée à la meilleure réponse:

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


export default (state, action) =>
  rootReducer(action.type === 'USER_LOGOUT' ? undefined : state, action);
Matt Carlotta
la source
cela fonctionne merci, j'étais de la réponse de Dan mais je ne peux pas le comprendre.
Aljohn Yamaro
14
 const reducer = (state = initialState, { type, payload }) => {

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

   return state
 }

Vous pouvez également déclencher une action gérée par tous ou certains réducteurs, que vous souhaitez réinitialiser au magasin initial. Une action peut déclencher une réinitialisation de l'ensemble de votre état, ou simplement une partie de celui-ci qui vous semble appropriée. Je pense que c'est la manière la plus simple et la plus contrôlable de le faire.

Daniel Petrov
la source
10

Avec Redux, j'ai appliqué la solution suivante, qui suppose que j'ai défini un état initial dans tous mes réducteurs (par exemple {utilisateur: {nom, email}}). Dans de nombreux composants, je vérifie ces propriétés imbriquées, donc avec ce correctif, j'évite que mes méthodes de rendu soient brisées sur les conditions de propriété couplées (par exemple, si state.user.email, qui générera une erreur, l'utilisateur n'est pas défini si les solutions mentionnées ci-dessus).

const appReducer = combineReducers({
  tabs,
  user
})

const initialState = appReducer({}, {})

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

  return appReducer(state, action)
}
Rob Moorman
la source
7

UPDATE NGRX4

Si vous migrez vers NGRX 4, vous avez peut-être remarqué dans le guide de migration que la méthode rootreducer pour combiner vos réducteurs a été remplacée par la méthode ActionReducerMap. Au début, cette nouvelle façon de faire pourrait rendre la réinitialisation de l'État difficile. C'est en fait simple, mais la façon de procéder a changé.

Cette solution est inspirée de la section API des méta-réducteurs des documents NGRX4 Github.

Tout d'abord, disons que vous combinez vos réducteurs comme celui-ci en utilisant la nouvelle option ActionReducerMap de 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
}

Supposons maintenant que vous souhaitiez réinitialiser l'état à partir de l'application.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 { }

"

Et c'est essentiellement une façon d'obtenir le même effet avec NGRX 4.

Tyler Brown
la source
5

En combinant les approches de Dan, Ryan et Rob, pour tenir compte de la conservation de l' routerétat et de l'initialisation de tout le reste dans l'arbre d'état, je me suis retrouvé avec ceci:

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

J'ai créé un composant pour donner à Redux la possibilité de réinitialiser l'état, il vous suffit d'utiliser ce composant pour améliorer votre magasin et d'envoyer un action.type spécifique pour déclencher la réinitialisation. La pensée de la mise en œuvre est la même que celle de @Dan Abramov.

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

wwayne
la source
4

J'ai créé des actions pour effacer l'état. Ainsi, lorsque j'envoie un créateur d'action de déconnexion, j'envoie également des actions pour effacer l'état.

Action d'enregistrement d'utilisateur

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

Créateur d'action de déconnexion

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

Réducteur

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

Je ne sais pas si c'est optimal?

Navinesh Chand
la source
2

Si vous utilisez des actions de redux , voici une solution rapide en utilisant un HOF ( Higher Order Function ) pour handleActions.

import { handleActions } from 'redux-actions';

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

Et puis utilisez handleActionsExau lieu de l'original handleActionspour gérer les réducteurs.

La réponse de Dan donne une excellente idée de ce problème, mais cela n'a pas bien fonctionné pour moi, car j'utilise redux-persist.
Lorsqu'il est utilisé avec redux-persist, le simple passage d'un undefinedétat ne déclenche pas un comportement persistant, je savais donc que je devais supprimer manuellement l'élément du stockage (React Native dans mon cas, donc AsyncStorage).

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

ou

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

n'a pas fonctionné pour moi non plus - ils ont juste crié après moi. (par exemple, se plaindre comme "_persist de clé inattendue ..." )

Puis j'ai soudain réfléchi à tout ce que je voulais, c'est que chaque réducteur individuel retourne son propre état initial lorsqu'un RESETtype d'action est rencontré. De cette façon, la persistance est gérée naturellement. Évidemment, sans la fonction utilitaire ci-dessus ( handleActionsEx), mon code ne sera pas sec (bien qu'il ne s'agisse que d'une seule ligne, c'est-à-dire RESET: () => initialState), mais je ne pouvais pas le supporter car j'adore la métaprogrammation.

elquimista
la source
2

La solution suivante a fonctionné pour moi.

J'ai ajouté la fonction de réinitialisation de l'état aux méta-réducteurs. La clé était d'utiliser

return reducer(undefined, action);

pour mettre tous les réducteurs à l'état initial. Le retour à la undefinedplace causait des erreurs car la structure du magasin avait été détruite.

/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 {}
Pan Piotr
la source
2

Du point de vue de la sécurité, la chose la plus sûre à faire lors de la connexion d' un utilisateur sur est de réinitialiser tous les états persistants (cookies ex, localStorage, IndexedDB, Web SQL, etc.) et de faire un rafraîchissement dur de la page à l' aide window.location.reload(). Il est possible qu'un développeur bâclé ait accidentellement ou intentionnellement stocké des données sensibles sur window, dans le DOM, etc. La suppression de tout état persistant et l'actualisation du navigateur est le seul moyen de garantir qu'aucune information de l'utilisateur précédent n'est divulguée à l'utilisateur suivant.

(Bien sûr, en tant qu'utilisateur sur un ordinateur partagé, vous devez utiliser le mode "navigation privée", fermer la fenêtre du navigateur vous-même, utiliser la fonction "effacer les données de navigation", etc., mais en tant que développeur, nous ne pouvons pas nous attendre à ce que tout le monde soit toujours cette diligence)

tlrobinson
la source
1
Pourquoi les gens ont-ils voté contre cela? Lorsque vous effectuez un nouvel état redux, avec un contenu vide, vous avez fondamentalement toujours les états précédents en mémoire et vous pouvez théoriquement accéder aux données à partir d'eux. Actualiser le navigateur EST votre pari le plus sûr!
Wilhelm Sorban
2

Ma solution de contournement lorsque je travaille avec dactylographiée, basée sur la réponse de Dan (les typages redux rendent impossible de passer undefinedau réducteur comme premier argument, donc je cache l'état racine initial dans une constante):

// 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"})
  }
}
Jacka
la source
2

La réponse acceptée m'a aidé à résoudre mon cas. Cependant, j'ai rencontré des cas où il ne fallait pas effacer tout l'État. Donc - je l'ai fait de cette façon:

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;

J'espère que ceci aide quelqu'un d'autre :)

pesho hristov
la source
que faire si j'ai plus de 10 états mais que je veux réinitialiser l'état d'un seul réducteur?
Paul
1

Juste une extension de la réponse @ dan-abramov , parfois nous pouvons avoir besoin de conserver certaines clés de la réinitialisation.

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);
};
gsaandy
la source
1

Une option rapide et facile qui fonctionnait pour moi était d'utiliser redux-reset . Ce qui était simple et propose également des options avancées pour les applications plus grandes.

Configuration dans 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)

Envoyez votre «réinitialisation» dans votre fonction de déconnexion

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

J'espère que cela t'aides

Munei Nengwenani
la source
1

Mon point de vue pour empêcher Redux de faire référence à la même variable de l'état initial:

// 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;
  }
};
Hani
la source
L'idée d'écrire l'état par défaut en tant que fonction a vraiment sauvé la journée ici. Merci 🙏
Chris Paul
0

Cette approche est très juste: détruisez tout état spécifique "NAME" pour ignorer et garder les autres.

const rootReducer = (state, action) => {
    if (action.type === 'USER_LOGOUT') {
        state.NAME = undefined
    }
    return appReducer(state, action)
}
khalil zaidoun
la source
Si vous n'avez besoin de réinitialiser qu'une seule partie de votre arbre d'état, vous pouvez également écouter USER_LOGOUTce réducteur et le gérer à cet endroit.
Andy_D
0

pourquoi ne pas simplement utiliser 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;
}

Remarque: assurez-vous de définir la valeur par défaut de l'action sur {}et vous êtes ok parce que vous ne voulez pas rencontrer d'erreur lorsque vous vérifiez action.typeà l'intérieur de l'instruction switch.

Alnamrouti à prix réduit
la source
0

J'ai trouvé que la réponse acceptée fonctionnait bien pour moi, mais elle a déclenché l' no-param-reassignerreur ESLint - https://eslint.org/docs/rules/no-param-reassign

Voici comment je l'ai géré à la place, en veillant à créer une copie de l'état (ce qui est, à ma connaissance, la chose Reduxy à faire ...):

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

Mais peut-être que créer une copie de l'état pour simplement le passer dans une autre fonction de réduction qui en crée une copie est un peu trop compliqué? Cela ne se lit pas aussi bien, mais est plus pertinent:

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

En plus de la réponse de Dan Abramov, ne devrions-nous pas définir explicitement action comme action = {type: '@@ INIT'} aux côtés de state = undefined. Avec le type d'action ci-dessus, chaque réducteur renvoie l'état initial.

rupav jain
la source
0

dans le serveur, j'ai une variable est: global.isSsr = true et dans chaque réducteur, j'ai un const est: initialState Pour réinitialiser les données dans le magasin, je fais ce qui suit avec chaque réducteur: exemple avec 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;
        }
    }
}

enfin sur le routeur du serveur:

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

La solution suivante fonctionne pour moi.

Tout d'abord au lancement de notre application, l'état du réducteur est frais et nouveau avec InitialState par défaut .

Nous devons ajouter une action qui appelle la charge initiale d'APP pour conserver l' état par défaut .

Lors de la déconnexion de l'application, nous pouvons simplement réaffecter l' état par défaut et le réducteur fonctionnera comme neuf .

Conteneur APP principal

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

Réducteur APP principal

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

Action de déconnexion lors de la réinitialisation de l'état

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

J'espère que ceci résoudra votre problème!

Mukundhan
la source
0

Pour que je réinitialise l'état à son état initial, j'ai écrit le code suivant:

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

Une chose que la solution dans la réponse acceptée ne fait pas est de vider le cache des sélecteurs paramétrés. Si vous avez un sélecteur comme celui-ci:

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

Ensuite, vous devrez les libérer à la déconnexion comme ceci:

selectTotal.release();

Sinon, la valeur mémorisée pour le dernier appel du sélecteur et les valeurs des derniers paramètres seront toujours en mémoire.

Les exemples de code proviennent des documents ngrx .

Grochni
la source
0

pour moi, ce qui a le mieux fonctionné, c'est de régler la initialStateplace de state:

  const reducer = createReducer(initialState,
  on(proofActions.cleanAdditionalInsuredState, (state, action) => ({
    ...initialState
  })),
noy levi
la source
-1

Une autre option consiste à:

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

'@@redux/INIT'est le type d'action que redux distribue automatiquement lorsque vous createStore, donc en supposant que tous vos réducteurs ont déjà une valeur par défaut, cela serait attrapé par ceux-ci et redémarrerait votre état. Cependant, cela peut être considéré comme un détail d'implémentation privé de redux, alors faites attention aux acheteurs ...

lobati
la source
J'ai fait que ça ne change pas d'état, j'ai aussi essayé @@ INIT qui est montré dans ReduxDevtools comme première action
Reza
-2

Faites simplement effacer votre session de lien de déconnexion et actualisez la page. Aucun code supplémentaire requis pour votre magasin. Chaque fois que vous souhaitez réinitialiser complètement l'état, une actualisation de page est un moyen simple et facilement reproductible de le gérer.

user3500325
la source
1
Que faire si vous utilisez un middleware qui synchronise le magasin avec le stockage local? Ensuite, votre approche ne fonctionne pas du tout ...
Spock
8
Je ne comprends pas vraiment pourquoi les gens votent contre des réponses comme celle-ci.
Wylliam Judd
Pourquoi les gens ont-ils voté contre cela? Lorsque vous effectuez un nouvel état redux, avec un contenu vide, vous avez fondamentalement toujours les états précédents en mémoire et vous pouvez théoriquement accéder aux données à partir d'eux. Actualiser le navigateur EST votre pari le plus sûr!
Wilhelm Sorban
-3

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

Nikita Beznosikov
la source
Explique? @SinanSamet
Nikita Beznosikov
Pour être clair, je n'ai pas voté contre. Mais je le décourage. Si vous accédez à la connexion et au rechargement, vous vous déconnecterez probablement oui. Mais seulement parce que vous avez perdu votre état. Sauf si vous utilisez redux-persist, auquel cas vous ne vous déconnecterez même pas. Dans l'ensemble, je déteste juste voir des fonctions commewindow.location.reload();
Sinan Samet
- N'écrivez pas de fonctions comme celle-ci. - Pourquoi? - Je n'aime pas ça.
Nikita Beznosikov
1
Le rechargement de la page est différent de la réinitialisation de l'état du magasin. De plus, vous pourriez être dans un environnement sans window, par exemple react-native
Max