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 ( u1
et u2
).
Imaginez la séquence d'événements suivante:
L'utilisateur se
u1
connecte à l'application et fait quelque chose, nous mettons donc en cache certaines données dans le magasin.L'utilisateur se
u1
déconnecte.L'utilisateur se
u2
connecte à 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?
Réponses:
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 uneUSER_LOGOUT
action, il renvoie à nouveau l'état initial.Par exemple, si votre réducteur de racine ressemblait à ceci:
Vous pouvez le renommer
appReducer
et lui écrire une nouvellerootReducer
délégation:Maintenant, nous avons juste besoin d'apprendre au nouveau
rootReducer
à retourner l'état initial après l'USER_LOGOUT
action. Comme nous le savons, les réducteurs sont censés retourner l'état initial lorsqu'ils sont appelés avecundefined
comme premier argument, quelle que soit l'action. Utilisons ce fait pour dépouiller conditionnellement l'accumuléstate
lorsque nous le transmettons àappReducer
:Désormais, chaque fois qu'un
USER_LOGOUT
incendie 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érifieraction.type
.Pour réitérer, le nouveau code complet ressemble à ceci:
Notez que je ne mute pas l'état ici, je réaffecte simplement la référence d'une variable locale appelée
state
avant 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
undefined
et de nettoyer chaque clé d'état de stockage.la source
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
action déclenchée, est-il possible d'obtenir des données d'état plus tôt? (par exemple via devtools)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
undefined
mais 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 packagela source
router
et NONrounting
combineReducers()
..... si vous l'aviez,combineReducers({routing: routingReducer})
ce serait comme décrit dans la réponseDéfinissez une action:
Ensuite, dans chacun de vos réducteurs, en supposant que vous utilisez
switch
ouif-else
pour gérer plusieurs actions via chaque réducteur. Je vais prendre l'affaire pour unswitch
.De cette façon, chaque fois que vous appelez une
RESET
action, 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:
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 unLOGOUT_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.la source
undefined
comme dans l'autre réponse. lorsque votre application attend un arbre d'état et que vous le lui donnez à laundefined
place, il y a juste plus de bugs et de maux de tête à gérer qu'un simple arbre vide.Juste une réponse simplifiée à la meilleure réponse:
la source
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.
la source
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).
la source
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:
Supposons maintenant que vous souhaitiez réinitialiser l'état à partir de l'application.module `
"
Et c'est essentiellement une façon d'obtenir le même effet avec NGRX 4.
la source
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:la source
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
la source
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
Créateur d'action de déconnexion
Réducteur
Je ne sais pas si c'est optimal?
la source
Si vous utilisez des actions de redux , voici une solution rapide en utilisant un HOF ( Higher Order Function ) pour
handleActions
.Et puis utilisez
handleActionsEx
au lieu de l'originalhandleActions
pour 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'unundefined
é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, doncAsyncStorage
).ou
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
RESET
type 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-à-direRESET: () => initialState
), mais je ne pouvais pas le supporter car j'adore la métaprogrammation.la source
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
pour mettre tous les réducteurs à l'état initial. Le retour à la
undefined
place causait des erreurs car la structure du magasin avait été détruite./reducers/index.ts
app.module.ts
la source
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' aidewindow.location.reload()
. Il est possible qu'un développeur bâclé ait accidentellement ou intentionnellement stocké des données sensibles surwindow
, 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)
la source
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
undefined
au réducteur comme premier argument, donc je cache l'état racine initial dans une constante):la source
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:
J'espère que ceci aide quelqu'un d'autre :)
la source
Juste une extension de la réponse @ dan-abramov , parfois nous pouvons avoir besoin de conserver certaines clés de la réinitialisation.
la source
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
Envoyez votre «réinitialisation» dans votre fonction de déconnexion
J'espère que cela t'aides
la source
Mon point de vue pour empêcher Redux de faire référence à la même variable de l'état initial:
la source
Cette approche est très juste: détruisez tout état spécifique "NAME" pour ignorer et garder les autres.
la source
USER_LOGOUT
ce réducteur et le gérer à cet endroit.pourquoi ne pas simplement utiliser
return module.exports.default()
;)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érifiezaction.type
à l'intérieur de l'instruction switch.la source
J'ai trouvé que la réponse acceptée fonctionnait bien pour moi, mais elle a déclenché l'
no-param-reassign
erreur ESLint - https://eslint.org/docs/rules/no-param-reassignVoici 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 ...):
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:
la source
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.
la source
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 :
enfin sur le routeur du serveur:
la source
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
Réducteur APP principal
Action de déconnexion lors de la réinitialisation de l'état
J'espère que ceci résoudra votre problème!
la source
Pour que je réinitialise l'état à son état initial, j'ai écrit le code suivant:
la source
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:
Ensuite, vous devrez les libérer à la déconnexion comme ceci:
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 .
la source
pour moi, ce qui a le mieux fonctionné, c'est de régler la
initialState
place destate
:la source
Une autre option consiste à:
'@@redux/INIT'
est le type d'action que redux distribue automatiquement lorsque vouscreateStore
, 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 ...la source
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.
la source
la source
window.location.reload();
window
, par exemple react-native