Come resettare lo stato di un negozio Redux?


455

Sto usando Redux per la gestione dello stato.
Come ripristinare il negozio al suo stato iniziale?

Ad esempio, supponiamo che io abbia due account utente ( u1e u2).
Immagina la seguente sequenza di eventi:

  1. L'utente u1accede all'app e fa qualcosa, quindi memorizziamo nella cache alcuni dati nello store.

  2. L'utente si u1disconnette.

  3. L'utente u2accede all'app senza aggiornare il browser.

A questo punto, i dati memorizzati nella cache verranno associati u1e vorrei eliminarli.

Come posso ripristinare il negozio Redux al suo stato iniziale quando il primo utente si disconnette?


8
Probabilmente è meglio cancellare lo stato al logout (dal punto di vista della sicurezza)
Clarkie,

Risposte:


1033

Un modo per farlo sarebbe quello di scrivere un riduttore di radice nella tua applicazione.

Il riduttore di radice delegherebbe normalmente la gestione dell'azione al riduttore generato da combineReducers(). Tuttavia, ogni volta che riceve USER_LOGOUTun'azione, restituisce di nuovo lo stato iniziale.

Ad esempio, se il tuo riduttore di radice appariva così:

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

È possibile rinominarlo appReducere scrivere un nuovo rootReducerdelegato ad esso:

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

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

Ora dobbiamo solo insegnare al nuovo rootReducera restituire lo stato iniziale dopo l' USER_LOGOUTazione. Come sappiamo, i riduttori dovrebbero restituire lo stato iniziale quando vengono chiamati undefinedcome primo argomento, indipendentemente dall'azione. Usiamo questo fatto per eliminare condizionalmente l'accumulato statementre lo passiamo a appReducer:

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

  return appReducer(state, action)
}

Ora, ogni volta che si USER_LOGOUTspara, tutti i riduttori verranno inizializzati di nuovo. Possono anche restituire qualcosa di diverso rispetto a prima se lo desiderano, perché possono anche controllare action.type.

Per ribadire, il nuovo codice completo è simile al seguente:

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

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

  return appReducer(state, action)
}

Nota che non sto mutando lo stato qui, sto semplicemente riassegnando il riferimento di una variabile locale chiamata stateprima di passarlo a un'altra funzione. La mutazione di un oggetto statale sarebbe una violazione dei principi di Redux.

Nel caso in cui si utilizzi redux-persist , potrebbe essere necessario pulire anche la memoria. Redux-persist mantiene una copia del tuo stato in un motore di archiviazione e la copia dello stato verrà caricata da lì al momento dell'aggiornamento.

Innanzitutto, è necessario importare il motore di archiviazione appropriato , quindi analizzare lo stato prima di impostarlo su undefinede pulire ogni chiave dello stato di archiviazione.

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
Sono curioso Dan, potresti anche fare qualcosa del genere nel tuo riduttore. con CLEAR_DATA come azione. case 'CLEAR_DATA': return initialState
HussienK,

6
@HussienK che funzionerebbe ma non sullo stato di ogni riduttore.
Cory Danielson,

12
Ecco una versione in cui si combinano dinamicamente i riduttori nel caso in cui si utilizzino riduttori asincroni: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
Questo approccio cancella completamente lo stato e tutta la sua storia? Sto pensando dal punto di vista della sicurezza: se questo è stato implementato, una volta che l' USER_LOGOUTazione è stata lanciata, è possibile ottenere dati sullo stato in precedenza? (es. via devtools)
AlexKempton

81

Vorrei sottolineare che il commento accettato da Dan Abramov è corretto, tranne per il fatto che abbiamo riscontrato uno strano problema durante l'utilizzo del pacchetto reagire-router-redux insieme a questo approccio. La nostra soluzione consisteva nel non impostare lo stato su undefinedma piuttosto utilizzare ancora il riduttore di routing corrente. Quindi suggerirei di implementare la soluzione di seguito se stai usando questo pacchetto

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

19
Penso che il takeaway qui sia che potresti non voler cancellare l'intero albero di stato al logout - l'approccio funziona ugualmente bene sul riduttore di radice di qualsiasi sottostruttura, e quindi potrebbe essere più chiaro applicare questa tecnica solo ai riduttori di radice del sottostruttura / i che si desidera eliminare, piuttosto che selezionare i bambini "speciali" per non eliminare il riduttore di radice dell'intero albero, in questo modo
davnicwil,

1
Penso che sto riscontrando questi problemi a cui ti riferisci ora (dove al logout imposterà il percorso sulla strada giusta ma caricherà un componente completamente diverso) Ho implementato qualcosa di simile al tuo per risolverlo, ma penso che qualcosa con immutabile js lo sta ammassando. Ho finito per creare un riduttore genitore che ha un'azione RESET-STATE e eredito da quel riduttore per evitare di toccare il routing del tutto
Neta Meta

Si sono verificati problemi simili, questo è stato risolto. Grazie.
Lloyd Watkin,

3
Si noti che con la reattanza-redux-router, la proprietà è routere NONrounting
Mrchief

2
@Mrchief dipende da come lo hai definito nel tuo combineReducers()..... se lo avessi combineReducers({routing: routingReducer})sarebbe come descritto nella risposta
Ben Lonsdale

40

Definisci un'azione:

const RESET_ACTION = {
  type: "RESET"
}

Quindi, in ciascuno dei riduttori, supponendo che si stia utilizzando switcho if-elseper gestire più azioni attraverso ciascun riduttore. Prenderò il caso per a 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; 
  }
}

In questo modo ogni volta che si chiama RESETazione, il riduttore aggiornerà il negozio con lo stato predefinito.

Ora, per il logout è possibile gestire i seguenti aspetti:

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

Ogni volta che un utente accede, senza un aggiornamento del browser. Il negozio sarà sempre predefinito.

store.dispatch(RESET_ACTION)elabora l'idea. Molto probabilmente avrai un creatore di azioni per lo scopo. Un modo molto migliore sarà quello di avere un LOGOUT_ACTION.

Una volta spedito questo LOGOUT_ACTION. Un middleware personalizzato può quindi intercettare questa azione, con Redux-Saga o Redux-Thunk. In entrambi i modi, tuttavia, è possibile inviare un'altra azione "RESET". In questo modo il logout e il reset del negozio avverranno in modo sincrono e il tuo negozio sarà pronto per un altro accesso utente.


1
Sento che questo è l'approccio migliore rispetto a impostare lo stato su undefinedcome piace nell'altra risposta. quando la tua applicazione si aspetta un albero di stato e tu lo dai undefinedinvece, ci sono solo più bug e mal di testa da affrontare di un semplice albero vuoto.
worc,

3
@worc In realtà lo stato non sarà indefinito, perché i riduttori restituiscono inizialeStato quando ricevono uno stato indefinito
Guillaume

3
@worc pensa che con questo approccio, ogni volta che qualcuno creerà un nuovo riduttore, dovrai ricordarti di aggiungere il caso di ripristino.
Francute,

1
ho decisamente cambiato idea su questo, per entrambi questi motivi, oltre all'idea che un RESET_ACTION sia un'azione . quindi non appartiene davvero al riduttore per cominciare.
worc,

1
Questo è sicuramente l'approccio giusto. Impostare lo stato su qualsiasi cosa tranne che sullo Stato iniziale sta solo chiedendo problemi
Sebastian Serrano,

15

Solo una risposta semplificata alla migliore risposta:

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


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

questo funziona grazie, ero dalla risposta di Dan ma non riesco a capirlo.
Aljohn Yamaro,

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

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

   return state
 }

Puoi anche lanciare un'azione che è gestita da tutti o da alcuni riduttori, che vuoi ripristinare al negozio iniziale. Un'azione può innescare un ripristino di tutto il tuo stato, o solo una parte di esso che ti sembra adatta. Credo che questo sia il modo più semplice e più controllabile per farlo.


10

Con Redux ho applicato la seguente soluzione, che presuppone che abbia impostato uno stato iniziale in tutti i miei riduttori (ad es. {Utente: {nome, email}}). In molti componenti controllo queste proprietà nidificate, quindi con questa correzione prevedo che i miei metodi di rendering vengano interrotti in base a condizioni di proprietà accoppiate (ad es. Se state.user.email, che genererà un errore utente non è definito se le soluzioni menzionate sopra).

const appReducer = combineReducers({
  tabs,
  user
})

const initialState = appReducer({}, {})

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

  return appReducer(state, action)
}

7

AGGIORNAMENTO NGRX4

Se stai migrando su NGRX 4, potresti aver notato dalla guida alla migrazione che il metodo rootreducer per combinare i tuoi riduttori è stato sostituito con il metodo ActionReducerMap. Inizialmente, questo nuovo modo di fare le cose potrebbe rendere il ripristino dello stato una sfida. In realtà è semplice, ma il modo di farlo è cambiato.

Questa soluzione è ispirata alla sezione API dei meta-riduttori dei documenti Github di NGRX4.

Innanzitutto, supponiamo che tu stia combinando i tuoi riduttori in questo modo usando la nuova opzione ActionReducerMap di 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
}

Ora, supponiamo che tu voglia ripristinare lo stato dall'interno di 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 { }

`

E questo è fondamentalmente un modo per ottenere lo stesso effetto con NGRX 4.


5

Combinando gli approcci di Dan, Ryan e Rob, per tenere conto dello routerstato e inizializzare tutto il resto nella struttura statale, ho finito con questo:

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

4

Ho creato un componente per dare a Redux la possibilità di ripristinare lo stato, devi solo utilizzare questo componente per migliorare il tuo negozio e inviare un action.type specifico per attivare il ripristino. Il pensiero dell'implementazione è lo stesso di quello che ha detto @Dan Abramov.

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


4

Ho creato azioni per cancellare lo stato. Pertanto, quando invio un creatore di azioni di disconnessione, invio anche le azioni per cancellare lo stato.

Azione di registrazione dell'utente

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

Creatore di azioni di logout

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

Reducer

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

Non sono sicuro che sia ottimale?


2

Se stai usando azioni redux , ecco una soluzione alternativa usando un HOF ( Higher Order Function ) per handleActions.

import { handleActions } from 'redux-actions';

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

E quindi utilizzare al handleActionsExposto dell'originale handleActionsper gestire i riduttori.

La risposta di Dan dà una grande idea su questo problema, ma non ha funzionato bene per me, perché sto usando redux-persist.
Se usato con redux-persist, semplicemente passare lo undefinedstato non ha innescato comportamenti persistenti, quindi sapevo che dovevo rimuovere manualmente l'oggetto dalla memoria (React Native nel mio caso, quindi AsyncStorage).

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

o

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

non ha funzionato neanche per me - mi hanno semplicemente urlato contro. (ad es. lamentarsi come "Chiave inaspettata _persist ..." )

Poi, all'improvviso, ho pensato che tutto ciò che volevo è fare in modo che ogni singolo riduttore ritorni il proprio stato iniziale quando RESETsi incontra un tipo di azione. In questo modo, la persistenza viene gestita in modo naturale. Ovviamente senza la funzione di utilità sopra ( handleActionsEx), il mio codice non sembrerà ASCIUTTO (anche se è solo un liner, cioè RESET: () => initialState), ma non potrei sopportarlo perché amo la metaprogrammazione.


2

La seguente soluzione ha funzionato per me.

Ho aggiunto la funzione di ripristino dello stato ai meta riduttori. La chiave era da usare

return reducer(undefined, action);

per impostare tutti i riduttori sullo stato iniziale. Il ritorno undefinedinvece causava errori dovuti al fatto che la struttura del negozio era stata distrutta.

/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

Dal punto di vista della sicurezza, la cosa più sicura da fare quando si accede un out utente è quello di ripristinare tutte stato persistente (ex biscotti, localStorage, IndexedDB, Web SQL, ecc) e fare un aggiornamento forzato della pagina utilizzando window.location.reload(). È possibile che uno sviluppatore sciatto abbia archiviato accidentalmente o intenzionalmente alcuni dati sensibili su window, nel DOM, ecc. Eliminare tutti gli stati persistenti e aggiornare il browser è l'unico modo per garantire che nessuna informazione dell'utente precedente venga divulgata all'utente successivo.

(Naturalmente, come utente su un computer condiviso dovresti usare la modalità "navigazione privata", chiudere tu stesso la finestra del browser, utilizzare la funzione "cancella dati di navigazione", ecc., Ma come sviluppatore non possiamo aspettarci che tutti lo siano sempre che diligente)


1
Perché le persone hanno votato in negativo? Quando si esegue un nuovo stato di redux, con contenuto vuoto, praticamente si hanno ancora in memoria gli stati precedenti e si potrebbe teoricamente accedere ai dati da essi. L'aggiornamento del browser È la scommessa più sicura!
Wilhelm Sorban,

2

La mia soluzione alternativa quando si lavora con dattiloscritto, basata sulla risposta di Dan (le tipizzazioni redux rendono impossibile passare undefinedal riduttore come primo argomento, quindi memorizzo nella cache lo stato iniziale iniziale in una costante):

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

La risposta accettata mi ha aiutato a risolvere il mio caso. Tuttavia, ho riscontrato casi in cui non è stato necessario chiarire l'intero stato. Quindi - l'ho fatto in questo modo:

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;

Spero che questo aiuti qualcun altro :)


cosa succede se ho più di 10 stati ma voglio ripristinare lo stato di un solo riduttore?
Paul,

1

Solo un'estensione alla risposta di @ dan-abramov , a volte potremmo aver bisogno di trattenere alcune chiavi dall'essere resettate.

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

Un'opzione semplice e veloce che ha funzionato per me è stata usare il redux-reset . Che era semplice e ha anche alcune opzioni avanzate, per le app più grandi.

Installazione in crea negozio

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)

Invia il tuo "reset" nella funzione di disconnessione

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

Spero che sia di aiuto


1

Il mio intento di impedire a Redux di fare riferimento alla stessa variabile dello stato iniziale:

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

L'idea di scrivere lo stato predefinito come funzione ha davvero salvato il giorno qui. Grazie
Chris

0

Questo approccio è molto giusto: Distruggi qualsiasi stato specifico "NAME" per ignorare e mantenere gli altri.

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

Se hai solo bisogno di ripristinare un pezzo dell'albero di stato, puoi anche ascoltare USER_LOGOUTquel riduttore e gestirlo lì.
Andy_D,

0

perché non usi semplicemente 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;
}

Nota: assicurati di impostare il valore predefinito dell'azione su {}e stai bene perché non vuoi riscontrare errori quando controlli action.typeall'interno dell'istruzione switch.


0

Ho scoperto che la risposta accettata ha funzionato bene per me, ma ha scatenato l' no-param-reassignerrore ESLint - https://eslint.org/docs/rules/no-param-reassign

Ecco come l'ho gestito invece, assicurandomi di creare una copia dello stato (che è, nella mia comprensione, la cosa da fare di Reduxy ...):

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

Ma forse creare una copia dello stato per passarlo in un'altra funzione di riduzione che ne crea una copia è un po 'complicato? Questo non è altrettanto chiaro, ma è più preciso:

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

0

Oltre alla risposta di Dan Abramov, non dovremmo impostare esplicitamente l'azione come action = {type: '@@ INIT'} insieme a state = undefined. Con il tipo di azione sopra riportato, ogni riduttore restituisce lo stato iniziale.


0

nel server, ho una variabile è: global.isSsr = true e in ogni riduttore, ho una const è: initialState Per reimpostare i dati nello Store, faccio quanto segue con ogni Reducer: esempio con 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;
        }
    }
}

finalmente sul router del server:

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

0

La seguente soluzione funziona per me.

Innanzitutto all'avvio della nostra applicazione lo stato del riduttore è nuovo e nuovo con InitialState predefinito .

Dobbiamo aggiungere un'azione che richiama il carico iniziale dell'APP per mantenere lo stato predefinito .

Durante la disconnessione dall'applicazione, è possibile semplicemente riassegnare lo stato predefinito e il riduttore funzionerà come nuovo .

Contenitore APP principale

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

Riduttore APP principale

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

Alla disconnessione dall'azione di chiamata per ripristinare lo stato

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

Spero che questo risolva il tuo problema!


0

Per ripristinare lo stato iniziale, ho scritto il seguente codice:

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

0

Una cosa che la soluzione nella risposta accettata non fa è cancellare la cache per i selettori con parametri. Se hai un selettore come questo:

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

Quindi dovresti rilasciarli al logout in questo modo:

selectTotal.release();

In caso contrario, il valore memorizzato per l'ultima chiamata del selettore e i valori degli ultimi parametri saranno ancora in memoria.

Gli esempi di codice provengono dai documenti di ngrx .


0

per me ciò che ha funzionato meglio è impostare initialStateinvece di state:

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

-1

Un'altra opzione è:

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

'@@redux/INIT'è il tipo di azione che redux invia automaticamente quando tu createStore, quindi supponendo che tutti i tuoi riduttori abbiano già un valore predefinito, questo verrebbe catturato da questi e ricomincerebbe il tuo stato. Tuttavia, potrebbe essere considerato un dettaglio dell'implementazione privata di redux, quindi il compratore sta attento ...


Ho fatto che non cambia stato, ho anche provato @@ INIT che viene mostrato in ReduxDevtools come prima azione
Reza,

-2

Basta avere il collegamento di logout cancellare la sessione e aggiornare la pagina. Nessun codice aggiuntivo necessario per il tuo negozio. Ogni volta che si desidera ripristinare completamente lo stato, un aggiornamento della pagina è un modo semplice e facilmente ripetibile per gestirlo.


1
Cosa succede se si utilizza un middleware che sincronizza il negozio con lo store locale? Quindi il tuo approccio non funziona affatto ...
Spock,

8
Non capisco davvero perché le persone sottovalutino risposte come questa.
Wylliam Judd,

Perché le persone hanno votato in negativo? Quando si esegue un nuovo stato di redux, con contenuto vuoto, praticamente si hanno ancora in memoria gli stati precedenti e si potrebbe teoricamente accedere ai dati da essi. L'aggiornamento del browser È la scommessa più sicura!
Wilhelm Sorban,

-3

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


spiegare? @SinanSamet
Nikita Beznosikov,

Per essere chiari, non ho votato in negativo. Ma lo scoraggio. Se navighi per accedere e ricaricare, probabilmente disconnetterai sì. Ma solo perché hai perso il tuo stato. A meno che tu non usi redux-persist, nel qual caso non ti disconnetterai nemmeno. Nel complesso, odio vedere funzioni comewindow.location.reload();
Sinan Samet,

- Non scrivere funzioni come questa. - Perché? - Non mi piace.
Nikita Beznosikov

1
Il ricaricamento della pagina è diverso dal ripristino dello stato del negozio. Inoltre, potresti essere in un ambiente senza window, ad esempio, reazione nativa
Max
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.