Lưu Redux state vào local storage với redux-persist

Redux Persist nhận trạng thái Redux object của bạn và lưu trữ nó trong bộ nhớ cục bộ. Khi ứng dụng khởi động, nó sẽ lấy trạng thái này và lưu trở lại Redux.

Bắt đầu sử dụng

Dependencies

npm install -save redux-persist – hoặc – yarn add redux-persist

Implementation

Khi tạo Redux store, truyền vào hàm createStore của bạn một hàm persistReducer để gói reducer gốc trong ứng dụng của bạn. Sau khi đã tạo xong store, truyền nó vào hàm persistStore để đảm bảo trạng thái Redux sẽ được lưu trữ mỗi khi nó thay đổi.

// src/store/index.js import { createStore } from ‘redux’; import { persistStore, persistReducer } from ‘redux-persist’; import storage from ‘redux-persist/lib/storage’; import autoMergeLevel2 from ‘redux-persist/lib/stateReconciler/autoMergeLevel2’; import rootReducer from ‘./reducers’; // Giá trị trả về từ combineReducers const persistConfig = { key: ‘root’, storage: storage, stateReconciler: autoMergeLevel2 // Xem thêm tại mục “Quá trình merge”. }; const pReducer = persistReducer(persistConfig, rootReducer); export const store = createStore(pReducer); export const persistor = persistStore(store);

Nếu bạn đang sử dụng React, hãy bao bọc root component của bạn trong PersistGate. Nó sẽ trì hoãn quá trình hiển thị giao diện người dùng của bạn cho đến khi trạng thái đã được lấy ra và lưu trở lại trong Redux.

import React from ‘react’; import { Provider } from ‘react-redux’; import { PersistGate } from ‘redux-persist/lib/integration/react’; import { persistor, store } from ‘./store’; import { RootComponent, LoadingView } from ‘./components’; const App = () => { return ( <Provider store={store}> // 2 props loading và persistor đều yêu cầu phải có <PersistGate loading={<LoadingView />} persistor={persistor}> <RootComponent /> </PersistGate> </Provider> ); }; export default App;

Tùy chỉnh dữ liệu lưu trữ

Nếu bạn không muốn lưu trạng thái Redux một phần nào đó, bạn có thể thêm nó vào danh sách loại trừ (blacklist). Danh sách loại trừ sẽ được thêm vào đối tượng cấu hình mà chúng ta sử dụng để cài đặt PersistReducer.

const persistConfig = { key: ‘root’, storage: storage, blacklist: [‘navigation’] }; const pReducer = persistReducer(persistConfig, rootReducer); export const store = createStore(pReducer); export const persistor = persistStore(store);

Danh sách loại trừ nhận vào một mảng chuỗi. Mỗi chuỗi phải khớp với một phần của trạng thái được quản lý bởi reducer mà bạn truyền vào persistReducer. Với ví dụ trên, nếu rootReducer được tạo thông qua hàm combineReducers, chúng ta dự kiến ​​navigatio là một trong các reducer như sau:

Có Thể Bạn Quan Tâm :  

combineReducers({ auth: AuthReducer, navigation: NavReducer, notes: NotesReducer });

Ngược lại, nếu bạn chỉ muốn lưu một phần nào đó của trạng thái, bạn có thể sử dụng whitelist. Ví dụ, với các reducer trên, chúng ta chỉ muốn lưu auth:

const persistConfig = { key: ‘root’, storage: storage, whitelist: [‘auth’] };

Vậy nếu chúng ta muốn loại trừ một thuộc tính lồng nhau thì sao? Ví dụ, giả sử đối tượng trạng thái của auth có một thuộc tính currentUser và bạn muốn lưu auth.currentUser nhưng không lưu auth.isLoggingIn. Để làm điều này, hãy gói AuthReducer vào một PersistReducer, và sau đó thêm isLoggingIn vào danh sách loại trừ.

// AuthReducer.js import storage from ‘redux-persist/lib/storage’; import { persistReducer } from ‘redux-persist’; const INITIAL_STATE = { currentUser: null, isLoggingIn: false }; const AuthReducer = (state = INITIAL_STATE, action) => { // Cài đặt reducer }; const persistConfig = { key: ‘auth’, storage: storage, blacklist: [‘isLoggingIn’] }; export default persistReducer(persistConfig, AuthReducer);

Nếu bạn muốn có tất cả các cấu hình được đặt cùng một nơi, hãy đưa chúng vào hàm combineReducers thay vì đưa nó vào các reducer tương ứng:

// src/reducers/index.js import { combineReducers } from ‘redux’; import storage from ‘redux-persist/lib/storage’; import { persistReducer } from ‘redux-persist’; import { authReducer, navReducer, notesReducer } from ‘./reducers’ const rootPersistConfig = { key: ‘root’, storage: storage, blacklist: [‘navigation’] }; const authPersistConfig = { key: ‘auth’, storage: storage, blacklist: [‘isLoggingIn’] }; const rootReducer = combineReducers({ auth: persistReducer(authPersistConfig, authReducer), navigation: navReducer, notes: notesReducer }); export default persistReducer(rootPersistConfig, rootReducer);

Quá trình merge

Khi ứng dụng của bạn khởi động, Redux sẽ đặt một trạng thái mặc định. Ngay sau đó, Redux Persist sẽ lấy trạng thái đã lưu từ bộ nhớ. Sau đó, trạng thái này sẽ được ghi đè lên trạng thái mặc định.

Quá trình merge có thể được tự động hoạt động. Tuy nhiên, bạn cũng có thể có những xử lý riêng cho quá trình này. Ví dụ, trong các phiên bản cũ hơn của Redux Persist, chúng ta phải tự quản lý quá trình khôi phục trạng thái (rehydration) bằng cách xử lý hành động REHYDRATE trong reducer và sau đó lưu dữ liệu của hành động này vào Redux state.

import { REHYDRATE } from ‘redux-persist’; const INITIAL_STATE = { currentUser: null, isLoggingIn: false }; const AuthReducer = (state = INITIAL_STATE, action) => { switch (action.type) { case REHYDRATE: return { …state, currentUser: action.payload.currentUser }; // …xử lý những trường hợp khác

Có Thể Bạn Quan Tâm :   Hướng dẫn sử dụng dịch vụ VnTopup cho khách hàng Agribank

Hành động REHYDRATE sẽ được dispatch bởi Redux Persist ngay sau khi nó đã lấy được trạng thái từ bộ nhớ. Nếu bạn trả về một đối tượng trạng thái mới từ hành động REHYDRATE, nó sẽ là trạng thái cuối cùng bạn nhận được. Tuy nhiên, như đã đề cập ở trên, trong các phiên bản mới của Redux Persist, bạn không cần phải xử lý hành động bằng tay nữa, trừ khi bạn muốn kiểm soát cách trạng thái được khôi phục như thế nào.

Tuy nhiên, hãy cẩn thận…

Có một vấn đề bạn cần biết trong quá trình merge, đó là mức độ sâu của quá trình merge. Trên, chúng ta đã biết quá trình merge chỉ đơn giản là thay đổi trạng thái cao nhất. Trong mã, nó sẽ trông giống như sau:

const finalState = { …initialState }; finalState[‘auth’] = persistedState[‘auth’] finalState[‘notes’] = persistedState[‘notes’]

Ở hầu hết các trường hợp, điều này đủ, nhưng nếu bạn phát hành phiên bản mới của ứng dụng và đặt trạng thái mặc định sau này như sau:

const INITIAL_STATE = { currentUser: null, isLoggingIn: false, error: ” };

Bạn chắc chắn muốn trạng thái cuối cùng chứa cả khóa lỗi này. Tuy nhiên, trạng thái đã được lưu không có khóa này và nó sẽ hoàn toàn thay đổi trạng thái mặc định trong quá trình merge. Tạm biệt, khóa lỗi.

Cách khắc phục cho trường hợp này là yêu cầu PersistReducer merge với độ sâu 2 cấp. Trong phần Bắt đầu sử dụng, bạn có thể đã thấy cài đặt stateReconciler trong root PersistReducer.

import autoMergeLevel2 from ‘redux-persist/lib/stateReconciler/autoMergeLevel2’; const persistConfig = { key: ‘root’, storage: storage, ** stateReconciler: autoMergeLevel2** };

autoMergeLevel2 là cấu hình để merge 2 cấp. Đối với trạng thái auth, điều này có nghĩa là quá trình merge đầu tiên sẽ tạo một bản sao của trạng thái mặc định của auth và sau đó chỉ ghi đè các khóa trong đối tượng auth nếu chúng đã được lưu trữ. Vì khóa lỗi chưa được lưu trữ, nó sẽ được giữ nguyên.

Tổng kết, bạn cần nhớ rằng PersistReducers mặc định sẽ sử dụng autoMergeLevel1, có nghĩa là nó sẽ thay đổi các trạng thái cao nhất bằng những gì đã được lưu trữ. Nếu bạn không có Redux Persist riêng để quản lý việc lưu trạng thái cho những khóa cao nhất này, bạn nhất định sẽ cần sử dụng autoMergeLevel2. Tôi thích tự cấu hình quá trình merge và không sử dụng hàm này, nhưng tùy thuộc vào bạn.

Có Thể Bạn Quan Tâm :   Heat pump là gì ?

Tùy chỉnh nâng cao

Các biến đổi (Transforms)

Biến đổi cho phép bạn tùy biến đối tượng trạng thái bạn đã lưu và khôi phục.

Khi đối tượng trạng thái được lưu, nó được tuần tự hóa bằng JSON.stringify(). Nếu có các thành phần trong đối tượng trạng thái không thể ánh xạ với đối tượng JSON, quá trình tuần tự hóa có thể biến đổi những thành phần này theo cách chúng ta không thể dự đoán được. Ví dụ, kiểu Set trong JavaScript không tồn tại trong JSON. Khi bạn tuần tự hóa một Set bằng JSON.stringify(), nó sẽ trở thành một đối tượng rỗng. Chắc chắn đây không phải là điều bạn muốn.

Dưới đây là một Transform có thể lưu trữ một thuộc tính thuộc kiểu Set một cách đơn giản bằng cách chuyển đổi nó thành một mảng và ngược lại. Với cách này, Set sẽ được chuyển đổi thành Array, một cấu trúc dữ liệu được hỗ trợ trong JSON. Khi được lấy ra từ local storage, mảng sẽ được chuyển đổi ngược lại thành Set trước khi lưu vào Redux store.

import { createTransform } from ‘redux-persist’; const SetTransform = createTransform( // Transform trạng thái trước khi nó được tuần tự hóa và lưu (inboundState, key) => { // Chuyển đổi mySet thành một mảng. return { …inboundState, mySet: […inboundState.mySet] }; }, // Transform trạng thái đang được khôi phục (outboundState, key) => { // Chuyển đổi mySet trở lại thành Set. return { …outboundState, mySet: new Set(outboundState.mySet) }; }, // Định nghĩa reducer nào sẽ áp dụng transform này. { whitelist: [‘someReducer’] } ); export default SetTransform;

Hàm createTransform nhận vào 3 tham số:

  • Một hàm được gọi ngay trước khi lưu trạng thái.
  • Một hàm được gọi ngay trước khi khôi phục trạng thái.
  • Một đối tượng cấu hình

Sau đó, transform cần được thêm vào đối tượng cấu hình của PersistReducer:

import storage from ‘redux-persist/lib/storage’; import { SetTransform } from ‘./Transforms’; const persistConfig = { key: ‘root’, storage: storage, transforms: [SetTransform] }; // …cài đặt còn lại

Bài viết được chuyển ngữ từ The Definitive Guide to Redux Persist của tác giả Mark Newton.

Back to top button