[javascript] Redux 앱에서 localStorage에 쓸 위치는 어디입니까?

상태 트리의 일부를 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'))
                       : {}

그런 다음이 persistedState상수를 다음 createStore과 같이 메소드에 전달 하십시오.

const store = createStore(
  reducer, 
  persistedState,
  /* any middleware... */
)


답변

한마디로 : 미들웨어.

redux-persist를 확인하십시오 . 또는 직접 작성하십시오.

[업데이트 2016. 12. 18] 현재 비활성 또는 더 이상 사용되지 않는 두 개의 유사한 프로젝트에 대한 언급을 제거하도록 편집되었습니다.


답변

위의 솔루션에 문제가있는 사람은 직접 작성할 수 있습니다. 내가 한 일을 보여 드리겠습니다. saga middleware두 가지 localStorageMiddlewarereHydrateStore방법 에 중점을 두는 것은 무시하십시오 . 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 초마다 상태를 업데이트하려는 경우이 방법이 도움이 될 수 있습니다.

  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 초마다 업데이트됩니다.


답변

다른 답변 (및 Jam Creencia ‘s Medium article ) 에서 제공되는 우수한 제안과 짧은 코드 발췌를 바탕 으로 완벽한 솔루션이 있습니다!

로컬 스토리지와 상태를 저장 /로드하는 2 개의 함수가 포함 된 파일이 필요합니다.

// FILE: src/common/localStorage/localStorage.js

// Pass in Redux store's state to save it to the user's browser local storage
export const saveState = (state) =>
{
  try
  {
    const serializedState = JSON.stringify(state);
    localStorage.setItem('state', serializedState);
  }
  catch
  {
    // We'll just ignore write errors
  }
};



// Loads the state and returns an object that can be provided as the
// preloadedState parameter of store.js's call to configureStore
export const loadState = () =>
{
  try
  {
    const serializedState = localStorage.getItem('state');
    if (serializedState === null)
    {
      return undefined;
    }
    return JSON.parse(serializedState);
  }
  catch (error)
  {
    return undefined;
  }
};

이러한 함수는 store.js 에서 가져 와서 상점을 구성합니다.

참고 : 하나의 종속성을 추가해야합니다. npm install lodash.throttle

// FILE: src/app/redux/store.js

import { configureStore, applyMiddleware } from '@reduxjs/toolkit'

import throttle from 'lodash.throttle';

import rootReducer from "./rootReducer";
import middleware from './middleware';

import { saveState, loadState } from 'common/localStorage/localStorage';


// By providing a preloaded state (loaded from local storage), we can persist
// the state across the user's visits to the web app.
//
// READ: https://redux.js.org/recipes/configuring-your-store
const store = configureStore({
	reducer: rootReducer,
	middleware: middleware,
	enhancer: applyMiddleware(...middleware),
	preloadedState: loadState()
})


// We'll subscribe to state changes, saving the store's state to the browser's
// local storage. We'll throttle this to prevent excessive work.
store.subscribe(
	throttle( () => saveState(store.getState()), 1000)
);


export default store;

상점은 index.js 로 가져 오므로 App.js 를 랩핑하는 제공자로 전달 될 수 있습니다 .

// FILE: src/index.js

import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'

import App from './app/core/App'

import store from './app/redux/store';


// Provider makes the Redux store available to any nested components
render(
	<Provider store={store}>
		<App />
	</Provider>,
	document.getElementById('root')
)

절대 가져 오기에는 YourProjectFolder / jsconfig.json에 대한 변경이 필요합니다 . 처음에는 파일을 찾을 수없는 경우 파일을 찾을 위치를 알려줍니다. 그렇지 않으면 src 외부에서 무언가를 가져 오려고 시도하는 것에 대한 불만이 표시 됩니다.

{
  "compilerOptions": {
    "baseUrl": "src"
  },
  "include": ["src"]
}