状態ツリーの一部をlocalStorageに永続化したいのですが。適切な場所は何ですか?減力剤か行為か。
還元剤は純粋で副作用がないため、これを行うのに適した場所ではありません。
私はただ加入者でそれをすることをお勧めします:
store.subscribe(() => {
// persist your state
})
ストアを作成する前に、これらの持続部分を読んでください。
const persistedState = // ...
const store = createStore(reducer, persistedState)
combineReducers()
を使用すると、状態を受け取らなかったリデューサーはデフォルトのstate
引数値を使用して通常どおり「起動」することに気付くでしょう。これはかなり便利です。
LocalStorageへの書き込みが速すぎないように、またはパフォーマンスに問題があるように、サブスクライバをデバウンスすることをお勧めします。
最後に、それをカプセル化したミドルウェアを代替手段として作成することもできますが、私は加入者から始めることにしました。なぜなら、それはより単純なソリューションであり、うまく機能するからです。
Dan Abramovの答えの空白を埋めるには、store.subscribe()
を次のように使うことができます。
store.subscribe(()=>{
localStorage.setItem('reduxState', JSON.stringify(store.getState()))
})
ストアを作成する前に、localStorage
を確認して、キーの下にあるJSONを次のように解析します。
const persistedState = localStorage.getItem('reduxState') ? JSON.parse(localStorage.getItem('reduxState')) : {}
次に、このperistedState
メソッドを次のようにcreateStore
メソッドに渡します。
const store = createStore(
reducer,
persistedState,
/* any middleware... */
)
一言で言えば:ミドルウェア。
チェックアウト redux-persist 。または自分で書いてください。
[2016年12月18日更新] 2つの類似したプロジェクトが非アクティブまたは非推奨になったという言及を削除するように編集しました。
誰かが上記の解決策で何か問題を抱えているならば、あなたはあなた自身に書くことができます。私がしたことをお見せしましょう。 saga middleware
を無視するということは、localStorageMiddleware
とreHydrateStore
という2つの方法に焦点を絞るだけです。 localStorageMiddleware
はすべてのredux state
をプルしてlocal storage
に入れ、rehydrateStore
はローカルストレージにある場合はすべてのapplicationState
をプルしてredux store
に入れる
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;
私は@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
)
);
違いは、アクションを保存しているだけなので、デバウンス関数を使用して自分の状態の最後のインタラクションのみを保存することができます。
少し遅れましたが、ここで説明する例に従って永続的な状態を実装しました。 X秒ごとにのみ状態を更新する場合、このアプローチは次の場合に役立ちます。
ラッパー関数を定義する
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!")
}
}
サブスクライバーでラッパー関数を呼び出す
store.subscribe((state) =>
{
updateLocalStorage(store.getState())
});
この例では、更新がトリガーされる頻度に関係なく、状態は最大 5秒ごとに更新されます。