[reactjs] 새로 고침시 redux 상태 트리를 유지하려면 어떻게해야합니까?

Redux 문서의 첫 번째 원칙은 다음과 같습니다.

전체 응용 프로그램의 상태는 단일 저장소 내의 개체 트리에 저장됩니다.

그리고 실제로 모든 원리를 잘 이해하고 있다고 생각했습니다. 하지만 이제 응용 프로그램이 무엇을 의미하는지 혼란 스럽습니다.

응용 프로그램이 웹 사이트의 작은 복잡한 부분 중 하나를 의미하고 한 페이지에서 작동한다면 이해합니다. 하지만 애플리케이션이 전체 웹 사이트를 의미한다면 어떨까요? 상태 트리를 유지하기 위해 LocalStorage 나 쿠키 등을 사용해야합니까? 하지만 브라우저가 LocalStorage를 지원하지 않으면 어떻게 될까요?

개발자가 상태 트리를 유지하는 방법을 알고 싶습니다! 🙂



답변

브라우저를 새로 고치는 동안 redux 상태를 유지하려면 redux 미들웨어를 사용하는 것이 가장 좋습니다. 아웃 확인 REDUX을-유지 하고 REDUX 저장 미들웨어. 둘 다 redux 상태를 저장하는 동일한 작업을 수행하여 마음대로 저장하고로드 할 수 있습니다.

편집하다

이 질문을 다시 검토 한 지 얼마되지 않았지만 다른 질문 (더 많은 찬성 답변이긴하지만)이 자신의 솔루션을 롤링하도록 장려하는 것을보고 다시 대답 할 것이라고 생각했습니다.

이 편집 시점에서 두 라이브러리는 지난 6 개월 이내에 업데이트되었습니다. 우리 팀은 몇 년 동안 프로덕션에서 redux-persist를 사용해 왔으며 아무런 문제가 없었습니다.

단순한 문제처럼 보일 수 있지만 자체 솔루션을 롤링하면 유지 관리 부담이 발생할뿐만 아니라 버그 및 성능 문제가 발생한다는 것을 금방 알게 될 것입니다. 가장 먼저 떠오르는 예는 다음과 같습니다.

  1. JSON.stringifyJSON.parse수뿐만 아니라 상처 성능이 필요하지만 응용 프로그램을 중단 할 수 REDUX 매장과 같은 코드의 중요한 부분에 처리되지 않은 때 오류가 발생하지 않을 때.
  2. (아래 답변에서 부분적으로 언급 됨) : 앱 상태를 저장하고 복원하는시기와 방법을 파악하는 것은 간단한 문제가 아닙니다. 너무 자주하면 성능이 저하됩니다. 충분하지 않거나 잘못된 상태 부분이 지속되면 더 많은 버그가 발생할 수 있습니다. 위에서 언급 한 라이브러리는 접근 방식에 대한 테스트를 거쳤으며 동작을 사용자 지정하는 몇 가지 확실한 방법을 제공합니다.
  3. redux (특히 React 생태계에서)의 아름다움의 일부는 여러 환경에 배치 할 수 있다는 것입니다. 이 편집 시점 에서 redux-persist에는 멋진 웹용 localForage 라이브러리 와 React Native, Electron 및 Node에 대한 지원을 포함하여 15 개의 서로 다른 저장소 구현이 있습니다.

요약 하자면 , 3kB 축소 + gzip (이 편집 당시)의 경우 이것은 문제가되지 않습니다. 팀에 스스로 해결하도록 요청합니다.


답변

2019 년 8 월 25 일 수정

의견 중 하나에서 언급했듯이. 원래 redux-storage 패키지가 react-stack 으로 이동되었습니다 . 이 접근 방식은 여전히 ​​자체 상태 관리 솔루션을 구현하는 데 중점을 둡니다.


원래 답변

제공된 답변이 어느 시점에서 유효했지만 원래 redux-storage 패키지가 더 이상 사용되지 않으며 더 이상 유지되지 않는다는 점에 유의하는 것이 중요합니다 .

redux-storage 패키지의 원래 작성자는 프로젝트를 더 이상 사용하지 않기로 결정했으며 더 이상 유지 관리하지 않습니다.

이제 미래에 이와 같은 문제를 피하기 위해 다른 패키지에 대한 종속성을 갖고 싶지 않은 경우 자체 솔루션을 롤링하는 것이 매우 쉽습니다.

당신이해야 할 일은 :

1- 상태를 반환하는 함수를 localStorage만든 다음 createStore저장소를 수화하기 위해 두 번째 매개 변수의 redux 함수에 상태를 전달합니다.

 const store = createStore(appReducers, state);

2- 상태 변경을 듣고 상태가 변경 될 때마다 상태를 localStorage

store.subscribe(() => {
    //this is just a function that saves state to localStorage
    saveState(store.getState());
});

그리고 그게 다야 … 실제로는 비슷한 것을 프로덕션에서 사용하지만 함수를 사용하는 대신 아래와 같이 매우 간단한 클래스를 작성했습니다.

class StateLoader {

    loadState() {
        try {
            let serializedState = localStorage.getItem("http://contoso.com:state");

            if (serializedState === null) {
                return this.initializeState();
            }

            return JSON.parse(serializedState);
        }
        catch (err) {
            return this.initializeState();
        }
    }

    saveState(state) {
        try {
            let serializedState = JSON.stringify(state);
            localStorage.setItem("http://contoso.com:state", serializedState);

        }
        catch (err) {
        }
    }

    initializeState() {
        return {
              //state object
            }
        };
    }
}

그런 다음 앱을 부트 스트랩 할 때 …

import StateLoader from "./state.loader"

const stateLoader = new StateLoader();

let store = createStore(appReducers, stateLoader.loadState());

store.subscribe(() => {
    stateLoader.saveState(store.getState());
});

누군가에게 도움이되기를 바랍니다.

성능 참고

애플리케이션에서 상태 변경이 매우 빈번한 경우 로컬 스토리지에 너무 자주 저장하면 애플리케이션 성능이 저하 될 수 있습니다. 특히 직렬화 / 비 직렬화 할 상태 개체 그래프가 큰 경우에는 더욱 그렇습니다. 이러한 경우 RxJs, lodash또는 이와 유사한 것을 사용하여 상태를 localStorage에 저장하는 함수를 디 바운스하거나 제한 할 수 있습니다 .


답변

이것은 Leo의 답변을 기반으로합니다 (타사 라이브러리를 사용하지 않고 질문의 목적을 달성하기 때문에 허용되는 답변이어야 함).

Redux Store만들고 로컬 저장소를 사용하여 유지하며 getter를 통해 저장소에 대한 간단한 액세스를 허용 하는 Singleton 클래스를 만들었습니다 .

이를 사용하려면 기본 클래스 주위에 다음 Redux-Provider 요소를 배치하십시오.

// ... Your other imports
import PersistedStore from "./PersistedStore";

ReactDOM.render(
  <Provider store={PersistedStore.getDefaultStore().store}>
    <MainClass />
  </Provider>,
  document.getElementById('root')
);

프로젝트에 다음 클래스를 추가하십시오.

import {
  createStore
} from "redux";

import rootReducer from './RootReducer'

const LOCAL_STORAGE_NAME = "localData";

class PersistedStore {

  // Singleton property
  static DefaultStore = null;

  // Accessor to the default instance of this class
  static getDefaultStore() {
    if (PersistedStore.DefaultStore === null) {
      PersistedStore.DefaultStore = new PersistedStore();
    }

    return PersistedStore.DefaultStore;
  }

  // Redux store
  _store = null;

  // When class instance is used, initialize the store
  constructor() {
    this.initStore()
  }

  // Initialization of Redux Store
  initStore() {
    this._store = createStore(rootReducer, PersistedStore.loadState());
    this._store.subscribe(() => {
      PersistedStore.saveState(this._store.getState());
    });
  }

  // Getter to access the Redux store
  get store() {
    return this._store;
  }

  // Loading persisted state from localStorage, no need to access
  // this method from the outside
  static loadState() {
    try {
      let serializedState = localStorage.getItem(LOCAL_STORAGE_NAME);

      if (serializedState === null) {
        return PersistedStore.initialState();
      }

      return JSON.parse(serializedState);
    } catch (err) {
      return PersistedStore.initialState();
    }
  }

  // Saving persisted state to localStorage every time something
  // changes in the Redux Store (This happens because of the subscribe() 
  // in the initStore-method). No need to access this method from the outside
  static saveState(state) {
    try {
      let serializedState = JSON.stringify(state);
      localStorage.setItem(LOCAL_STORAGE_NAME, serializedState);
    } catch (err) {}
  }

  // Return whatever you want your initial state to be
  static initialState() {
    return {};
  }
}

export default PersistedStore;


답변