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


92

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

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

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

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

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


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

답변:


88

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

-

편집하다

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

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

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

  1. JSON.stringify및 JSON.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.