javascript - Redux स्टोर की स्थिति को कैसे रीसेट करें?




store redux-store (18)

Redux के साथ यदि निम्न समाधान लागू किया गया है, जो मानता है कि मैंने अपने सभी रिड्यूसर (उदाहरण {user: {name, email}}) में एक आरंभिक सेट किया है। कई घटकों में मैं इन नेस्टेड गुणों की जांच करता हूं, इसलिए इस फिक्स के साथ मैं अपने रेंडरर्स के तरीकों को कपल प्रॉपर्टी की शर्तों (जैसे कि state.user.email, जो एक त्रुटि उपयोगकर्ता को फेंक देगा, यदि ऊपरी उल्लेखित समाधान) अपरिभाषित है, पर टूट जाता है।

const appReducer = combineReducers({
  tabs,
  user
})

const initialState = appReducer({}, {})

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

  return appReducer(state, action)
}

मैं राज्य प्रबंधन के लिए Redux का उपयोग कर रहा हूं।
मैं स्टोर को उसकी प्रारंभिक स्थिति में कैसे रीसेट करूं?

उदाहरण के लिए, मान लें कि मेरे दो उपयोगकर्ता खाते हैं ( u1 और u2 )।
घटनाओं के निम्नलिखित अनुक्रम की कल्पना करें:

  1. उपयोगकर्ता u1 ऐप में लॉग इन करता है और कुछ करता है, इसलिए हम स्टोर में कुछ डेटा कैश करते हैं।

  2. उपयोगकर्ता u1 लॉग आउट करता है।

  3. उपयोगकर्ता u2 ब्राउज़र को रीफ्रेश किए बिना ऐप में लॉग इन करता है।

इस बिंदु पर, कैश्ड डेटा u1 संबद्ध होगा, और मैं इसे साफ करना चाहूंगा।

जब पहला उपयोगकर्ता लॉग आउट करता है तो मैं Redux स्टोर को उसकी प्रारंभिक स्थिति में कैसे रीसेट कर सकता हूं?


Redux को प्रारंभिक अवस्था के एक ही चर के संदर्भ में रखने के लिए मेरा उपाय:

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

आप सिर्फ return module.exports.default() उपयोग क्यों नहीं करते 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;
}

नोट: सुनिश्चित करें कि आपने एक्शन डिफॉल्ट मान {} सेट किया है और आप ठीक हैं क्योंकि आप स्विच स्टेटमेंट के अंदर action.type जांच करते समय त्रुटि का सामना नहीं करना चाहते हैं।


एक अन्य विकल्प यह है:

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

'@@redux/INIT' एक्शन टाइप है, जो आपके द्वारा createStore होने पर createStore अपने आप डिस्पैच हो createStore , इसलिए मान लें कि आपके reducers में पहले से ही कोई डिफ़ॉल्ट है, यह उन लोगों द्वारा पकड़ा जाएगा और अपने राज्य को नए सिरे से शुरू करेगा। यह Redux का एक निजी कार्यान्वयन विवरण माना जा सकता है, हालांकि, खरीदार सावधान रहें ...


ऐसा करने का एक तरीका यह होगा कि आप अपने आवेदन में एक रूट रिड्यूसर लिखें।

रूट combineReducers() आम तौर पर combineReducers() द्वारा उत्पन्न combineReducers() को एक्शन को हैंडल करने का काम करेगा। हालाँकि, जब भी इसे USER_LOGOUT कार्रवाई मिलती है, यह प्रारंभिक अवस्था को फिर से लौटा देता है।

उदाहरण के लिए, यदि आपका रूट reducer इस तरह दिखता है:

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

आप इसका नाम बदलकर appReducer और इसे प्रस्तुत करने appReducer एक नया rootReducer लिख सकते हैं:

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

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

अब हमें USER_LOGOUT कार्रवाई के बाद प्रारंभिक स्थिति को वापस करने के लिए नए rootReducer को पढ़ाने की आवश्यकता है। जैसा कि हम जानते हैं, रिड्यूसर प्रारंभिक स्थिति को वापस करने वाले होते हैं जब उन्हें पहले तर्क के रूप में undefined कहा जाता है, चाहे वह कोई भी कार्रवाई क्यों न हो। इस स्थिति का उपयोग सशर्त रूप से संचित state को appReducer लिए करते हैं क्योंकि हम इसे appReducer पास appReducer :

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

  return appReducer(state, action)
}

अब, जब भी USER_LOGOUT आग, सभी reducers को नए सिरे से शुरू किया जाएगा। वे शुरू में किए गए action.type तुलना में कुछ अलग लौटा सकते हैं यदि वे चाहते हैं क्योंकि वे action.type जांच कर सकते हैं।

दोबारा दोहराने के लिए, पूरा नया कोड इस तरह दिखता है:

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

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

  return appReducer(state, action)
}

ध्यान दें कि मैं यहां राज्य को उत्परिवर्तित नहीं कर रहा हूं, मैं इसे केवल एक अन्य फ़ंक्शन को पास करने से पहले state नामक स्थानीय चर के संदर्भ को फिर से असाइन कर रहा हूं। राज्य वस्तु को म्यूट करना Redux सिद्धांतों का उल्लंघन होगा।

यदि आप redux-persist का उपयोग कर रहे redux-persist , तो आपको अपने भंडारण को साफ करने की भी आवश्यकता हो सकती है। Redux-persist आपके राज्य की एक प्रति एक भंडारण इंजन में रखता है, और राज्य की प्रति वहां से ताज़ा करने पर लोड की जाएगी।

सबसे पहले, आपको उचित भंडारण इंजन को आयात करने की आवश्यकता है और फिर, प्रत्येक भंडारण राज्य कुंजी को undefined और साफ करने से पहले राज्य को पार्स करने के लिए।

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

टाइप के साथ काम करते समय मेरा वर्कअराउंड, डैन के उत्तर के ऊपर बनाया गया (रिडक्स टाइपिंग पहले तर्क के रूप में रिड्यूसर के लिए undefined पास करना असंभव बनाता है, इसलिए मैं प्रारंभिक रूट स्टेट को स्थिर रूप से कैश करता हूं):

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

निम्नलिखित समाधान ने मेरे लिए काम किया।

मैंने मेटा रिड्यूसर्स में स्टेट फ़ंक्शन रीसेट करना जोड़ा। कुंजी का उपयोग करना था

return reducer(undefined, action);

प्रारंभिक स्थिति में सभी reducers सेट करने के लिए। इसके बजाय undefined लौटने से इस तथ्य के कारण त्रुटियां हो रही थीं कि स्टोर की संरचना नष्ट हो गई है।

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

निम्नलिखित समाधान मेरे लिए काम करता है।

हमारे आवेदन के आरंभ होने पर सबसे पहले Reducer राज्य ताज़ा होता है और डिफ़ॉल्ट InitialState के साथ नया होता है

हमें एक कार्रवाई को जोड़ना होगा जो डिफ़ॉल्ट स्थिति को बनाए रखने के लिए एपीएटल लोड पर कॉल करता है।

एप्लिकेशन से लॉग आउट करते समय हम डिफॉल्ट स्टेट को रीसाइन कर सकते हैं और रिड्यूसर नए की तरह काम करेगा।

मुख्य एपीपी कंटेनर

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

मुख्य एपीपी Reducer

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

राज्य को रीसेट करने के लिए लॉगआउट कॉलिंग कार्रवाई पर

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

आशा है इससे तुम्हारी समस्या का समाधान हो गया होगा!


मेरे लिए काम करने वाला एक त्वरित और आसान विकल्प redux-reset का उपयोग कर रहा था। जो सीधा था और कुछ उन्नत विकल्प भी हैं, बड़े ऐप्स के लिए।

स्टोर बनाने में सेटअप

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)

अपने लॉगआउट फ़ंक्शन में अपना 'रीसेट' डिस्पैच करें

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

उम्मीद है की यह मदद करेगा


मेरे लिए राज्य को उसकी प्रारंभिक स्थिति में रीसेट करने के लिए, मैंने निम्नलिखित कोड लिखा:

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

मैंने Redux को राज्य को रीसेट करने की क्षमता देने के लिए एक घटक बनाया है, आपको बस अपने स्टोर को बढ़ाने और रीसेट को ट्रिगर करने के लिए एक विशिष्ट एक्शन टाइप को भेजने के लिए इस घटक का उपयोग करने की आवश्यकता है। कार्यान्वयन का विचार वैसा ही है जैसा @ दन अब्रामोव ने कहा था।

गिथब: https://github.com/wwayne/redux-reset


मैंने पाया कि स्वीकृत उत्तर ने मेरे लिए अच्छा काम किया, लेकिन इसने ESLint no-param-reassign रिअसाइन एरर को ट्रिगर किया - https://eslint.org/docs/rules/no-param-reassign

यहां बताया गया है कि मैंने इसके बजाय इसे कैसे संभाला, राज्य की एक प्रति (जो मेरी समझ में है, 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)
}

लेकिन हो सकता है कि राज्य की एक प्रति बनाने के लिए यह सिर्फ एक और reducer समारोह में पारित करने के लिए है कि एक प्रतिलिपि बनाता है एक छोटे से अधिक जटिल है? यह अच्छी तरह से पढ़ा नहीं है, लेकिन अधिक से अधिक बिंदु है:

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

यदि आप redux-actions का उपयोग कर रहे हैं, तो यहां handleActions लिए HOF ( हायर ऑर्डर फंक्शन ) का उपयोग करते हुए एक त्वरित समाधान है।

import { handleActions } from 'redux-actions';

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

और फिर reducers को संभालने के लिए मूल handleActions बजाय handleActionsEx उपयोग करें।

डैन का जवाब इस समस्या के बारे में एक महान विचार देता है, लेकिन यह मेरे लिए अच्छी तरह से काम नहीं करता था, क्योंकि मैं redux-persist का उपयोग कर रहा हूं।
जब redux-persist साथ प्रयोग किया जाता redux-persist , तो बस undefined स्थिति से गुजरना स्थायी व्यवहार को ट्रिगर नहीं करता है, इसलिए मुझे पता था कि मुझे स्टोरेज से आइटम को मैन्युअल रूप से हटाना होगा (इस मामले में AsyncStorage , इस प्रकार AsyncStorage )।

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

या

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

मेरे लिए भी काम नहीं किया - वे सिर्फ मुझ पर चिल्लाया। (उदाहरण के लिए, "अनपेक्षित कुंजी _पर्सिस्ट ..." जैसी शिकायत करना)

तब मैंने अचानक सोचा था कि मैं चाहता हूं कि हर व्यक्ति रिड्यूसर को अपनी प्रारंभिक अवस्था लौटा दे जब RESET कार्रवाई प्रकार सामने आए। इस तरह, स्थायी रूप से प्राकृतिक रूप से संभाला जाता है। स्पष्ट रूप से उपर्युक्त उपयोगिता फ़ंक्शन ( handleActionsEx ) के बिना, मेरा कोड DRY नहीं लगेगा (हालाँकि यह सिर्फ एक लाइनर है, अर्थात RESET: () => initialState ), लेकिन मैं इसे 'cuz I love RESET: () => initialState ' नहीं कह सकता।


यह दृष्टिकोण बहुत ही सही है: किसी भी विशिष्ट राज्य "NAME" को अनदेखा करना और दूसरों को बनाए रखना।

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

सबसे अच्छा जवाब के लिए एक सरलीकृत जवाब:

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


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

सर्वर में, मेरे पास एक वैरिएबल है: Global.isSsr = true और प्रत्येक रिड्यूसर में, मेरे पास एक कॉस्ट है: आरंभ में स्टोर में डेटा को रीसेट करने के लिए, मैं प्रत्येक Reducer के साथ निम्न कार्य करता हूं : 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;
        }
    }
}

अंत में सर्वर के राउटर पर:

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

सुरक्षा के दृष्टिकोण से, सबसे सुरक्षित बात यह है कि किसी उपयोगकर्ता को लॉग आउट करते समय सभी स्थिर स्थिति (पूर्व कुकीज़, localStorage , IndexedDB , Web SQL , आदि) को रीसेट करना है और window.location.reload() का उपयोग करके पृष्ठ का हार्ड रिफ्रेश करना है। । यह संभव है कि एक मैला डेवलपर गलती से या जानबूझकर window पर कुछ संवेदनशील डेटा संग्रहीत करता window , डोम आदि में, सभी लगातार स्थिति को दूर करना और ब्राउज़र को ताज़ा करना एकमात्र तरीका है जो पिछले उपयोगकर्ता से कोई जानकारी नहीं अगले उपयोगकर्ता पर लीक होने की गारंटी देता है।

(बेशक, एक साझा कंप्यूटर पर एक उपयोगकर्ता के रूप में आपको "निजी ब्राउज़िंग" मोड का उपयोग करना चाहिए, ब्राउज़र विंडो को स्वयं बंद करना चाहिए, "स्पष्ट ब्राउज़िंग डेटा" फ़ंक्शन का उपयोग करना चाहिए, आदि, लेकिन एक डेवलपर के रूप में हम हर किसी से हमेशा होने की उम्मीद नहीं कर सकते। वह मेहनती)


स्वीकृत जवाब से मुझे अपने मामले को सुलझाने में मदद मिली। हालाँकि, मुझे ऐसे मामले का सामना करना पड़ा जहाँ पूरे राज्य को साफ़ नहीं किया गया था। तो - मैंने इसे इस तरह किया:

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;

मनाइए कि यह किसी और के लिए सहायक हो :)





redux-store