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


92

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

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

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

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

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


2
그것은 일종의 광범위한 질문입니다. 언급 한 모든 것을 할 수 있습니다. 시도한 것과 실패한 것을 보여주기 위해 공유하고 싶은 코드가 있습니까? 전체 웹 사이트를 단일 엔티티로 구현하거나 여러 개를 가질 수 있습니다. localStorage를 사용하여 데이터를 유지하거나 실제 DB를 사용하거나 둘 다 사용할 수 없습니다. 적용은 살아 있고 활동적인 인스턴스를 의미합니다. 대부분의 경우 이것은 당신의 뿌리입니다. 그러나 응용 프로그램을 구현하는 방법에는 여러 가지가 있습니다.
ZekeDroid

답변:


88

브라우저를 새로 고치는 동안 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 (이 편집 당시)의 경우 이것은 문제가되지 않습니다. 팀에 스스로 해결하도록 요청합니다.


5
redux-persist (아직 redux-storage를 시도하지 않았 음)를 추천 할 수 있지만 구성과 설정이 거의 없어도 잘 작동합니다.
larrydahooster

이 날짜를 기준으로 두 라이브러리의 이음새는 죽은 상태이며 2 년 전의 마지막 커밋으로 유지되지 않습니다.
AnBisw 2011

1
redux-persist가 약간 돌아온 것 같습니다. 22 일 전 제 글을 쓰는 시점에 새로운
출판물이 있습니다

REDUX 저장의 새로운 위치는 github.com/react-stack/redux-storage
마이클 Freidgeim

2
이 답변에 대한 참고 사항 : 실제로 소프트웨어 및 라이브러리는 프로그래밍 언어의 매우 중요한 일부 모듈도 타사 / 라이브러리에서 지원하는 커뮤니티 (지원) 기반 접근 방식을 일반적으로 채택했습니다 . 일반적으로 개발자는 스택에서 사용되는 모든 도구를 주시하여 지원 중단 / 업데이트 여부를 확인해야합니다. 두 가지 선택; 1. 자신을 구현하고 성능 및 교차 플랫폼 표준을 보장하면서 영원히 개발하십시오. 2. 사용 전투 테스트 솔루션 및 MiFreidgeimSO - stopbeingevil 말한다 @로 업데이트 만 / 권장 사항을 확인
긱 가이

80

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에 저장하는 함수를 디 바운스하거나 제한 할 수 있습니다 .


11
미들웨어를 사용하는 대신이 접근 방식을 선호합니다. 성능 문제에 관한 팁을 주셔서 감사합니다.
Joe zhou

1
확실히 선호하는 대답입니다. 그러나 페이지를 새로 고침하고 스토어를 생성 할 때 localstorage에서 상태를로드하면 감속기가 수신 한 이전 상태에서 발견 된 예기치 않은 속성 [컨테이너 이름] 텍스트를 포함하는 몇 가지 경고가 표시됩니다. 대신 알려진 감속기 속성 이름 : "global", "language". 예기치 않은 속성은 무시됩니다. 여전히 작동하며 기본적으로 저장소를 만들 때 다른 모든 컨테이너에 대해 알지 못한다고 불평합니다. 이 경고 주위 방법은?
Zief

@Zief는 말하기 어렵다. 메시지 "보인다"는 매우 명확하고 감속기는 지정되지 않은 속성을 예상합니다. 직렬화 된 상태에 기본값을 제공하는 것과 관련된 것일 수 있습니까?
Leo

매우 간단한 솔루션입니다. 감사합니다.
Ishara Madawa 2011 년

1
@Joezhou는이 접근 방식을 선호하는 이유를 듣고 싶습니다. 개인적으로 이것은 미들웨어가 의도 한 것과 똑같은 것 같습니다.
michaelgmcd

1

이것은 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;

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.