reference: https://www.npmjs.com/package/redux-persist
Redux-Persist βοΈ
νλ‘μ νΈ μ§ν μ€(=μΌν μΉ΄νΈ λͺ©λ‘ νμ΄μ§), μλ‘κ³ μΉ¨μ μν΄ λ°μ΄ν°κ° μ΄κΈ°νλλ€λ λ¬Έμ μ μ λ°κ²¬νλ€. Redux-Persistλ λΈλΌμ°μ μ λ°μ΄ν°λ₯Ό μ μ₯ν μ μλλ‘ λλλ€.
μ΄μ»€λ¨Έμ€ μ¬μ΄νΈμ 'μ₯λ°κ΅¬λ λͺ©λ‘' λλ 'λ€ν¬λͺ¨λ' λ±, μ μ μ κ°μΈμ 보 μ μΆμ μν₯μ λ―ΈμΉλ λ°μ΄ν°κ° μλλΌλ©΄, λΈλΌμ°μ μ ν΄λΉ λ°μ΄ν°λ₯Ό μ μ₯νμ¬ μ¬μ©νλ κ²μ΄ λ μ 리νκ² λ€.
λ°λΌμ λ³Έ TILμμλ Redux-Persistκ° μ 곡νλ API λͺ κ°μ§λ₯Ό μ΄ν΄λ³΄κ³ κΈ°μ΄μ μΈ μ¬μ©λ²μ λν΄ κΈ°μ νκ² λ€. μΆκ°μ μΌλ‘ μ΄μ κΉμ§ μλν shopping cartμ λν μ λ°μ μΈ λ‘μ§μ μ 리νλλ‘ νκ² λ€.
// configureStore.js
import { createStore } from 'redux'
import { persistStore, persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage' // defaults to localStorage for web
import rootReducer from './reducers'
const persistConfig = {
key: 'root',
storage,
}
const persistedReducer = persistReducer(persistConfig, rootReducer)
export default () => {
let store = createStore(persistedReducer)
let persistor = persistStore(store)
return { store, persistor }
}
μ μ½λλ κΈ°λ³Έμ μΌλ‘ νλ‘μ νΈμ store.js νμΌμ μ λ ₯λ λ΄μ©μ΄λ€. APIμ λν΄μλ μ°¨ν μ΄ν΄λ³Ό μμ μ΄λ, ꡬ쑰λΆν° νμΈνκ² λ€.
persistConfigλΌλ κ°μ²΄κ° μ μΈλμ΄ μκ³ λ΄λΆμλ 'key:'root''μ 'storage'κ° μλ€. μ΄ κ°μ²΄λ₯Ό persistReducerλΌλ ν¨μμ 첫 λ²μ§Έ μΈμλ‘ μ λ¬νκ³ μλ€. λ λ²μ§Έ μΈμμλ rootReducerκ° μλ€.
persistReducer ν¨μλ₯Ό persistedReducerμ μ μ₯νκ³ , persistedReducerλ₯Ό createStore ν¨μλ₯Ό ν΅ν΄ storeμ ν λΉν λͺ¨μ΅μ΄λ€.
μΆκ°μ μΌλ‘ persistStoreλΌλ ν¨μμ μ storeλ₯Ό μΈμλ‘ μ λ¬νμ¬ persistorλΌλ λ³μμ ν λΉνλ€.
import { PersistGate } from 'redux-persist/integration/react'
// ... normal setup, create store and persistor, import components etc.
const App = () => {
return (
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<RootComponent />
</PersistGate>
</Provider>
);
};
λ£¨νΈ μ»΄ν¬λνΈλ₯Ό μ μ½λμ²λΌ PersistGateλ‘ κ°μΈλ©΄ μ΅μ’ μ μΌλ‘ λ°μ΄ν°κ° λΈλΌμ°μ μ μ μ₯λλ€.
persistReducer()λ λ κ°μ μΈμ(=config, store)λ₯Ό λ°λλ€.
config obj
config κ°μ²΄λ Redux-Persistμ λν μ€μ μ ν¬ν¨νλ κ°μ²΄λ€. keyλ Redux μνλ₯Ό μλ³ν λ μ¬μ©ν keyλ€. storageλ μ μ₯μ μ ν(=λ‘컬 μ€ν 리μ§, μΈμ μ€ν 리μ§)μ μλ―Ένλ€.
localStorageμ μ μ₯νκ³ μΆμΌλ©΄ import storage from 'redux-persist/lib/storage', session Storageμ μ μ₯νκ³ μΆμΌλ©΄ import storageSession from 'redux-persist/lib/storage/session'λ₯Ό μμ±νλ©΄ λλ€.
μμ½νμλ©΄ persistReducer()λ Redux-Persistμ λν μ€μ μ ν¬ν¨νλ κ°μ²΄μΈ configλ₯Ό μ λ¬λ°μμ, Redux μνλ₯Ό μ§μμ μΌλ‘ μ μ₯νκ³ λ³΅μν μ μλλ‘ νλ μλ‘μ΄ reducer
λ₯Ό λ°ννλ ν¨μλΌκ³ ν μ μλ€.
persistReducer()λ₯Ό ν΅ν΄ persistμ λν μ€μ μ΄ μ μ©λ reducerλ₯Ό λ§λ€μλ€. persistStore()λ ν΄λΉ λ΄μ©μ λ΄μμ, persistμ λν μ€μ μ΄ μ μ©λ store
λ₯Ό μμ±νλ€. 'persistStore(store)'λ persistor κ°μ²΄
λ€.
persistor κ°μ²΄λ Redux Persistμ persistStore ν¨μλ‘λΆν° λ°νλλ©°, μνλ₯Ό μꡬμ μΌλ‘ μ μ₯νκ³ λ³΅μνκΈ° μν μ¬λ¬ μ μ©ν λ©μλλ₯Ό μ 곡νλ€. purge, flush, pause, persistκ° μλλ° λ³Έ κΈμμλ μμΈν λ€λ£¨μ§ μλλ‘ νκ² λ€. μ μ©ν λ©μλκ° μλ€λ μ λ§ μμλκ³ λμ΄κ°μ.
total κ΄λ ¨ λ‘μ§ βοΈ
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import { splitPrice } from "../utils/commonModule";
export const getCartData = createAsyncThunk(
"cart/getCartData",
async (action) => {
console.log(action);
try {
return action;
} catch (error) {
console.error("Error: ", error);
throw error;
}
}
);
export const deleteCardData = createAsyncThunk(
"cart/deleteCardData",
async (productId) => {
try {
return productId;
} catch (error) {
console.error("Error: ", error);
throw error;
}
}
);
export const updateTotalPriceAndQuantity = createAsyncThunk(
"cart/updateTotalPriceAndQuantity",
async (_, { getState }) => {
const state = getState();
const { cartProduct } = state.cart;
const { totalPrice, totalQuantity } = cartProduct.reduce(
(acc, item) => {
acc.totalPrice += splitPrice(item.price) * item.count;
acc.totalQuantity += item.count;
return acc;
},
{ totalPrice: 0, totalQuantity: 0 }
);
return { totalPrice, totalQuantity };
}
);
const cartSlice = createSlice({
name: "cart",
initialState: {
cartProduct: [],
totalPrice: 0,
totalQuantity: 0,
},
reducers: {},
extraReducers: (builder) => {
builder.addCase(getCartData.fulfilled, (state, action) => {
const { category, id, count } = action.payload;
const existingProductIndex = state.cartProduct.findIndex(
(product) => product.category === category && product.id === id
);
if (existingProductIndex !== -1) {
state.cartProduct[existingProductIndex].count += count;
} else {
state.cartProduct.push(action.payload);
}
});
builder.addCase(deleteCardData.fulfilled, (state, action) => {
const { id, category } = action.payload;
const deleteIndex = state.cartProduct.findIndex(
(product) => product.category === category && product.id === id
);
if (deleteIndex !== -1) {
state.cartProduct.splice(deleteIndex, 1);
}
});
builder.addCase(updateTotalPriceAndQuantity.fulfilled, (state, action) => {
const { totalPrice, totalQuantity } = action.payload;
state.totalPrice = totalPrice;
state.totalQuantity = totalQuantity;
});
},
});
export default cartSlice.reducer;
μ΄μ μ§μ§ μλ²½νκ² κΈ°λ₯μ ꡬννλ€. νμ§λ§ λ΄μΌμ΄ μνμ΄κΈ°μ...
viewκΉμ§ μλ²½νκ² κ΅¬μ±ν΄μ μ΅μ’ μ μΌλ‘ μ 리νλλ‘ νκ² λ€!