Где писать в localStorage в приложении Redux?

Я хочу, чтобы некоторые части дерева состояния сохранялись в localStorage. Какое место для этого нужно? Редуктор или действие?

Ответ 1

Редуктор никогда не подходит для этого, потому что редукторы должны быть чистыми и не иметь побочных эффектов.

Я бы порекомендовал просто сделать это в подписчике:

store.subscribe(() => {
  // persist your state
})

Перед созданием магазина прочитайте эти сохраняемые части:

const persistedState = // ...
const store = createStore(reducer, persistedState)

Если вы используете combineReducers(), вы заметите, что редукторы, которые получили состояние, будут "загружаться" как обычно, используя значение аргумента по умолчанию state. Это может быть очень удобно.

Желательно, чтобы вы отклонили своего подписчика, чтобы вы не слишком быстро записывали на localStorage или у вас были проблемы с производительностью.

Наконец, вы можете создать промежуточное программное обеспечение, которое инкапсулирует это как альтернативу, но Id начнет с подписчика, потому что это более простое решение и хорошо выполняет работу.

Ответ 2

Чтобы заполнить пробелы ответа Дэн Абрамова, вы можете использовать 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... */
)

Ответ 3

Одним словом: middleware.

Отъезд redux-persist. Или напишите свой собственный.

[UPDATE 18 Dec 2016] Отредактировано, чтобы удалить упоминание о двух похожих проектах, которые теперь неактивны или устарели.

Ответ 4

Если у кого-то возникли проблемы с вышеупомянутыми решениями, вы можете написать свой собственный. Позвольте мне показать вам, что я сделал. Игнорируйте вещи saga middleware просто сосредоточьтесь на двух вещах localStorageMiddleware и методе reHydrateStore. localStorageMiddleware вытаскивает все redux state localStorageMiddleware и помещает его в 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;

Ответ 5

Я не могу ответить @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 для сохранения только последнего взаимодействия вашего состояния

Ответ 6

Я немного опоздал, но я реализовал постоянное состояние в соответствии с примерами, приведенными здесь. Если вы хотите обновлять состояние только каждые X секунд, этот подход может помочь вам:

  1. Определите функцию-обертку

    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!")
        }
    }
    
  2. Вызовите функцию-обертку у вашего подписчика

        store.subscribe((state) =>
        {
        updateLocalStorage(store.getState())
         });
    

В этом примере состояние обновляется не более каждые 5 секунд, независимо от того, как часто запускается обновление.