Redux 문서의 첫 번째 원칙은 다음과 같습니다.
전체 응용 프로그램의 상태는 단일 저장소 내의 개체 트리에 저장됩니다.
그리고 실제로 모든 원리를 잘 이해하고 있다고 생각했습니다. 하지만 이제 응용 프로그램이 무엇을 의미하는지 혼란 스럽습니다.
응용 프로그램이 웹 사이트의 작은 복잡한 부분 중 하나를 의미하고 한 페이지에서 작동한다면 이해합니다. 하지만 애플리케이션이 전체 웹 사이트를 의미한다면 어떨까요? 상태 트리를 유지하기 위해 LocalStorage 나 쿠키 등을 사용해야합니까? 하지만 브라우저가 LocalStorage를 지원하지 않으면 어떻게 될까요?
개발자가 상태 트리를 유지하는 방법을 알고 싶습니다! 🙂
답변
브라우저를 새로 고치는 동안 redux 상태를 유지하려면 redux 미들웨어를 사용하는 것이 가장 좋습니다. 아웃 확인 REDUX을-유지 하고 REDUX 저장 미들웨어. 둘 다 redux 상태를 저장하는 동일한 작업을 수행하여 마음대로 저장하고로드 할 수 있습니다.
–
편집하다
이 질문을 다시 검토 한 지 얼마되지 않았지만 다른 질문 (더 많은 찬성 답변이긴하지만)이 자신의 솔루션을 롤링하도록 장려하는 것을보고 다시 대답 할 것이라고 생각했습니다.
이 편집 시점에서 두 라이브러리는 지난 6 개월 이내에 업데이트되었습니다. 우리 팀은 몇 년 동안 프로덕션에서 redux-persist를 사용해 왔으며 아무런 문제가 없었습니다.
단순한 문제처럼 보일 수 있지만 자체 솔루션을 롤링하면 유지 관리 부담이 발생할뿐만 아니라 버그 및 성능 문제가 발생한다는 것을 금방 알게 될 것입니다. 가장 먼저 떠오르는 예는 다음과 같습니다.
JSON.stringify
및JSON.parse
수뿐만 아니라 상처 성능이 필요하지만 응용 프로그램을 중단 할 수 REDUX 매장과 같은 코드의 중요한 부분에 처리되지 않은 때 오류가 발생하지 않을 때.- (아래 답변에서 부분적으로 언급 됨) : 앱 상태를 저장하고 복원하는시기와 방법을 파악하는 것은 간단한 문제가 아닙니다. 너무 자주하면 성능이 저하됩니다. 충분하지 않거나 잘못된 상태 부분이 지속되면 더 많은 버그가 발생할 수 있습니다. 위에서 언급 한 라이브러리는 접근 방식에 대한 테스트를 거쳤으며 동작을 사용자 지정하는 몇 가지 확실한 방법을 제공합니다.
- 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;