我想将我的状态树的某些部分持久化到localStorage。在什么地方这样做比较合适?减速还是行动?


当前回答

我正在寻找一个关于如何使用redux-toolkit-persist将状态持久化到本地存储的完整示例,但没有成功,直到我遇到上面的@canProm响应来解决我的问题。 这就是对我有效的方法

//package.json
 "reduxjs-toolkit-persist": "^7.0.1",
"lodash": "^4.17.21"

 //localstorage.ts
 import localStorage from 'reduxjs-toolkit-persist/es/storage';
  export const saveState = (state: any) => {
  try {
     console.log(state);
     const serializableState = JSON.stringify(state);
     localStorage.setItem('globalState', serializableState);
   } catch (err) {
    console.log('Redux was not able to persist the state into the localstorage');
 }
};


export const loadState = () => {
  try {
    const serializableState: string | any = 
 localStorage.getItem('globalState');
    return serializableState !== null || serializableState === undefined ? JSON.parse(serializableState) : undefined;
   } catch (error) {
      return undefined;
  }
};



//slices - actions
//reduxjs-toolkit-slices.ts
  import { combineReducers, createSlice, PayloadAction } from '@reduxjs/toolkit';
  import { UserModel } from '../model/usermodel';
  import { GlobalState } from './type';

  const deaultState: GlobalState = {
       counter: 0,
       isLoggedIn: false
  };

 const stateSlice = createSlice({
    name: "state",
   initialState: deaultState,
reducers: {
    isLoggedIn: (state, action: PayloadAction<boolean>) => {
        console.log('isLogged');
        console.log(state.isLoggedIn);
        console.log(action);
        state.isLoggedIn = action.payload;
        console.log(state.isLoggedIn);
    },
    setUserDetails: (state, action: PayloadAction<UserModel>) => {
        console.log('setUserDetails');
        console.log(state);
        console.log(action);
        //state.userContext.user = action.payload;
    }
  }
});

//export actions under slices
export const {
  isLoggedIn: isUserLoggedAction,
  setUserDetails: setUserDetailActions
} = stateSlice.actions;

//TODO: use the optimal way for combining reducer using const
//combine reducer from all slice
export const combineReducer = combineReducers({
   stateReducer: stateSlice.reducer
});

 //storeConfig
  //reduxjs-toolkit-store.ts
 import { configureStore } from '@reduxjs/toolkit';
 import { throttle } from 'lodash';
 import { persistReducer } from 'reduxjs-toolkit-persist';
 import autoMergeLevel2 from 'reduxjs-toolkit-persist/lib/stateReconciler/autoMergeLevel2';
 import storage from 'reduxjs-toolkit-persist/lib/storage';
  import { loadState, saveState } from './localStorage';
 import { combineReducer } from './reduxjs-toolkit-slices';

  // persist config
  const persistConfig = {
      key: 'root',
      storage: storage,
      stateReconciler: autoMergeLevel2,
  };
  const persistedReducer = persistReducer(persistConfig, combineReducer);

  // export reducers under slices
  const store = configureStore({
      reducer: persistedReducer,
     devTools: process.env.NODE_ENV !== 'production',
     preloadedState: loadState(), //call loadstate method to initiate store from localstorage
     middleware: (getDefaultMiddleware) =>
        getDefaultMiddleware({
        thunk: true,
        serializableCheck: false,
    }),
 });
 // handle state update event. Whenever the state will change, this subscriber will call the saveState methode to update and persist the state into the store
store.subscribe(throttle(() => {
    saveState(store.getState());
 }, 1000));


export default store;

 //App.ts
 import { persistStore } from 'reduxjs-toolkit-persist';
 import { PersistGate } from 'reduxjs-toolkit-persist/integration/react';
 import './i18n';

 let persistor = persistStore(store);

  ReactDOM.render(
     <Provider store={store}>
       <PersistGate loading={<div>Loading .....</div>} persistor={persistor}>
         <HalocarburesRouter />
       </PersistGate>
     </Provider>,
   document.getElementById('ingenierieMdn'));

其他回答

我无法回答@Gardezi,但基于他的代码的选项可能是:

const rootReducer = combineReducers({
    users: authReducer,
});

const localStorageMiddleware = ({ getState }) => {
    return next => action => {
        const result = next(action);
        if ([ ACTIONS.LOGIN ].includes(result.type)) {
            localStorage.setItem(appConstants.APP_STATE, JSON.stringify(getState()))
        }
        return result;
    };
};

const reHydrateStore = () => {
    const data = localStorage.getItem(appConstants.APP_STATE);
    if (data) {
        return JSON.parse(data);
    }
    return undefined;
};

return createStore(
    rootReducer,
    reHydrateStore(),
    applyMiddleware(
        thunk,
        localStorageMiddleware
    )
);

不同之处在于我们只是保存了一些动作,你可以使用debounce函数来保存你状态的最后一次交互

如果你不需要复制所有redux store到localStorage,你可以使用特定的store参数:

store.subscribe(()=>{
  window.localStorage.setItem('currency', store.getState().lang)
})

并像这样设置初始状态参数值:

const initialState = {
  currency: window.localStorage.getItem('lang') ?? 'en',
}

在这种情况下,你不需要将const persistedState传递给const store = createStore()

如果任何人对上述解决方案有任何问题,您可以编写自己的解决方案。让我给你看看我做了什么。忽略saga中间件的事情,只关注localStorageMiddleware和reHydrateStore方法。localStorageMiddleware将所有的redux状态拉到本地存储中,rehydrateStore将所有的applicationState拉到本地存储中,如果存在的话,将其放入redux存储中

import {createStore, applyMiddleware} from 'redux'
import createSagaMiddleware from 'redux-saga';
import decoristReducers from '../reducers/decorist_reducer'

import sagas from '../sagas/sagas';

const sagaMiddleware = createSagaMiddleware();

/**
 * Add all the state in local storage
 * @param getState
 * @returns {function(*): function(*=)}
 */
const localStorageMiddleware = ({getState}) => { // <--- FOCUS HERE
    return (next) => (action) => {
        const result = next(action);
        localStorage.setItem('applicationState', JSON.stringify(
            getState()
        ));
        return result;
    };
};


const reHydrateStore = () => { // <-- FOCUS HERE

    if (localStorage.getItem('applicationState') !== null) {
        return JSON.parse(localStorage.getItem('applicationState')) // re-hydrate the store

    }
}


const store = createStore(
    decoristReducers,
    reHydrateStore(),// <-- FOCUS HERE
    applyMiddleware(
        sagaMiddleware,
        localStorageMiddleware,// <-- FOCUS HERE 
    )
)

sagaMiddleware.run(sagas);

export default store;

我正在寻找一个关于如何使用redux-toolkit-persist将状态持久化到本地存储的完整示例,但没有成功,直到我遇到上面的@canProm响应来解决我的问题。 这就是对我有效的方法

//package.json
 "reduxjs-toolkit-persist": "^7.0.1",
"lodash": "^4.17.21"

 //localstorage.ts
 import localStorage from 'reduxjs-toolkit-persist/es/storage';
  export const saveState = (state: any) => {
  try {
     console.log(state);
     const serializableState = JSON.stringify(state);
     localStorage.setItem('globalState', serializableState);
   } catch (err) {
    console.log('Redux was not able to persist the state into the localstorage');
 }
};


export const loadState = () => {
  try {
    const serializableState: string | any = 
 localStorage.getItem('globalState');
    return serializableState !== null || serializableState === undefined ? JSON.parse(serializableState) : undefined;
   } catch (error) {
      return undefined;
  }
};



//slices - actions
//reduxjs-toolkit-slices.ts
  import { combineReducers, createSlice, PayloadAction } from '@reduxjs/toolkit';
  import { UserModel } from '../model/usermodel';
  import { GlobalState } from './type';

  const deaultState: GlobalState = {
       counter: 0,
       isLoggedIn: false
  };

 const stateSlice = createSlice({
    name: "state",
   initialState: deaultState,
reducers: {
    isLoggedIn: (state, action: PayloadAction<boolean>) => {
        console.log('isLogged');
        console.log(state.isLoggedIn);
        console.log(action);
        state.isLoggedIn = action.payload;
        console.log(state.isLoggedIn);
    },
    setUserDetails: (state, action: PayloadAction<UserModel>) => {
        console.log('setUserDetails');
        console.log(state);
        console.log(action);
        //state.userContext.user = action.payload;
    }
  }
});

//export actions under slices
export const {
  isLoggedIn: isUserLoggedAction,
  setUserDetails: setUserDetailActions
} = stateSlice.actions;

//TODO: use the optimal way for combining reducer using const
//combine reducer from all slice
export const combineReducer = combineReducers({
   stateReducer: stateSlice.reducer
});

 //storeConfig
  //reduxjs-toolkit-store.ts
 import { configureStore } from '@reduxjs/toolkit';
 import { throttle } from 'lodash';
 import { persistReducer } from 'reduxjs-toolkit-persist';
 import autoMergeLevel2 from 'reduxjs-toolkit-persist/lib/stateReconciler/autoMergeLevel2';
 import storage from 'reduxjs-toolkit-persist/lib/storage';
  import { loadState, saveState } from './localStorage';
 import { combineReducer } from './reduxjs-toolkit-slices';

  // persist config
  const persistConfig = {
      key: 'root',
      storage: storage,
      stateReconciler: autoMergeLevel2,
  };
  const persistedReducer = persistReducer(persistConfig, combineReducer);

  // export reducers under slices
  const store = configureStore({
      reducer: persistedReducer,
     devTools: process.env.NODE_ENV !== 'production',
     preloadedState: loadState(), //call loadstate method to initiate store from localstorage
     middleware: (getDefaultMiddleware) =>
        getDefaultMiddleware({
        thunk: true,
        serializableCheck: false,
    }),
 });
 // handle state update event. Whenever the state will change, this subscriber will call the saveState methode to update and persist the state into the store
store.subscribe(throttle(() => {
    saveState(store.getState());
 }, 1000));


export default store;

 //App.ts
 import { persistStore } from 'reduxjs-toolkit-persist';
 import { PersistGate } from 'reduxjs-toolkit-persist/integration/react';
 import './i18n';

 let persistor = persistStore(store);

  ReactDOM.render(
     <Provider store={store}>
       <PersistGate loading={<div>Loading .....</div>} persistor={persistor}>
         <HalocarburesRouter />
       </PersistGate>
     </Provider>,
   document.getElementById('ingenierieMdn'));

我有点晚了,但是我根据这里的示例实现了一个持久状态。如果你想每X秒更新一次状态,这个方法可以帮助你:

Define a wrapper function let oldTimeStamp = (Date.now()).valueOf() const millisecondsBetween = 5000 // Each X milliseconds function updateLocalStorage(newState) { if(((Date.now()).valueOf() - oldTimeStamp) > millisecondsBetween) { saveStateToLocalStorage(newState) oldTimeStamp = (Date.now()).valueOf() console.log("Updated!") } } Call a wrapper function in your subscriber store.subscribe((state) => { updateLocalStorage(store.getState()) });

在这个例子中,状态最多每5秒更新一次,而不管更新被触发的频率。