https://light9639.github.io/Redux-Toolkit-Middleware/
β¨ Redux-Toolkit-Middleware ν νλ νμ΄μ§μ λλ€. β¨
- React μμ±
npm create-react-app my-app
# or
yarn create react-app my-app- viteλ₯Ό μ΄μ©νμ¬ νλ‘μ νΈλ₯Ό μμ±νλ €λ©΄
npm create vite@latest
# or
yarn create vite- ν°λ―Έλμμ μ€ν ν νλ‘μ νΈ μ΄λ¦ λ§λ ν React μ ν, Typescirpt μ ννλ©΄ μμ± μλ£.
- Redux-Toolkit μ€μΉ λͺ λ Ήμ΄
npm install redux react-redux @reduxjs/toolkit
# or
yarn add redux react-redux @reduxjs/toolkitreact-reduxμμProviderν¨μ κ°μ Έμ¨ νstore.tsνμΌμimportν ν<Provider store={store}></Provider>μΌλ‘<App />μ λλ¬μΈλ©΄Redux-Toolkitμ¬μ©μ€λΉ μλ£.
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import { Provider } from 'react-redux'
import store from './store/index'
import './index.css'
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
)slice.tsμμ κ°μ Έμ¨ ν¨μλ€μ μ¬μ©νμ¬stateκ°μ λ³κ²½μν¨λ€.index.tsμμ νμ μ΄ μ μΈλuseSelector,useDispatchμ κ°μ Έμ μ¬μ©νλ€.inputμ κ°μenteredμ ν λΉνμ¬inputμμ μ«μλ§νΌ κ°μ μ¦κ°μν¨λ€.
import { useState } from "react";
import ReactLogo from './assets/react.svg'
import { increment, decrement, incrementByAmount } from "./store/slice";
import { useSelector, useDispatch } from "./store/index";
import './App.css';
export default function App(): JSX.Element {
const count = useSelector((state) => state.counter.value);
const dispatch = useDispatch();
const [entered, setEntered] = useState("");
return (
<div className="App">
<div>
<a href="https://ko.redux.js.org/introduction/getting-started/" target="_blank">
<img src="https://camo.githubusercontent.com/7b7f04b16cc2d2d4a32985710e4d640985337a32bbb1e60cdacede2c8a4ae57b/68747470733a2f2f63646e2e776f726c64766563746f726c6f676f2e636f6d2f6c6f676f732f72656475782e737667" className="logo" alt="Redux logo" />
</a>
</div>
<h1>Redux Toolkit Middleware</h1>
<h2>Count: {count}</h2>
<div>
<button onClick={() => dispatch(increment())}>Increment +1</button>
<button onClick={() => dispatch(decrement())}>Decrement -1</button>
</div>
<div>
<label>
Increment By Amount :
<input
type="number"
step={1}
value={entered}
onChange={(e) => setEntered(e.target.value)}
className="Input"
/>
</label>
<button
onClick={() => dispatch(incrementByAmount(parseInt(entered, 10)))}
>
Go
</button>
</div>
<div className="card">
<p>
Edit <code>src/App.tsx</code> and save to test HMR
</p>
<p className="read-the-docs">
Click on the Redux logos to learn more
</p>
</div>
</div>
)
}- 리λμ€ λ―Έλ€μ¨μ΄λ₯Ό λ§λ€μ΄μ λ‘컬μ€ν λ¦¬μ§ κ°μ 체ν¬νμ¬ κ°μ΄ μλ€λ©΄ κ°μ Έμ€κ³ , μλ€λ©΄ μλ‘μ΄ κ°μ λΆμ¬νλ€.
import { createListenerMiddleware, isAnyOf } from "@reduxjs/toolkit";
import { decrement, increment, incrementByAmount } from "./slice";
import type { RootState } from "./index";
export const listenerMiddleware = createListenerMiddleware();
listenerMiddleware.startListening({
matcher: isAnyOf(increment, decrement, incrementByAmount),
effect: (action, listenerApi) =>
localStorage.setItem(
"count",
JSON.stringify((listenerApi.getState() as RootState).counter)
)
});- μ΄κΈ°κ°μ μΈν νκ³ νμ μ μ§μ ν΄μ€λ€.
reducersμstateλ³κ²½ ν¨μλ₯Ό μμ±ν λ€μcounterSlice.actionsλ₯Ό ν΅ν΄ λ΄λ³΄λ΄μ μ¬μ©ν μ μκ²λ νλ€.
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
// μ΄κΈ°κ° νμ
λ§λ€κΈ°
interface CounterState {
value: number;
}
// μ΄κΈ°κ° μΈν
const initialState: CounterState = { value: 0 };
const counterSlice = createSlice({
name: "counter",
initialState,
reducers: {
increment(state) {
state.value++;
},
decrement(state) {
state.value--;
},
incrementByAmount(state, action: PayloadAction<number>) {
state.value += action.payload;
}
}
});
// ν¨μ λ΄λ³΄λ΄κΈ°
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
// reducer λ΄λ³΄λ΄κΈ°
export default counterSlice.reducer;counterStateλ³μλ λ‘컬μ€ν 리μ§μμ κ°μ Έμ€λ μν μ νλ€.configureStoreλ₯Ό μ΄μ©νμ¬ μ€ν μ΄λ₯Ό μμ±νλ€.RootState,AppDispatchλΌλ νμ κ°μ κ°μ Έμ΄μΌλ‘μ¨ νμ μ μ§μ νλ€.useDispatch,useSelectorμ νμ μΆκ°νμ¬ νμ μ€μ νλ€.
import { AnyAction, configureStore } from "@reduxjs/toolkit";
import {
TypedUseSelectorHook,
useDispatch as _useDispatch,
useSelector as _useSelector
} from "react-redux";
import { listenerMiddleware } from "./middleware";
import slice from "./slice";
// λ‘컬μ€ν 리μ§μμ κ°μ Έμ€κΈ°
const counterState = JSON.parse(localStorage.getItem("count") || "null");
// μ€ν μ΄ μμ±
const store = configureStore({
preloadedState: {
counter: counterState === null ? { value: 0 } : counterState
},
reducer: {
counter: slice
},
middleware: (getDefaultMiddleware) => [
...getDefaultMiddleware(),
listenerMiddleware.middleware
]
});
// νμ
κ°μ Έμ€κΈ°
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
export type Action = AnyAction;
// useDispatch, useSelectorμ νμ
μΆκ°νμ¬ νμ
μ€μ
export const useDispatch: () => AppDispatch = _useDispatch;
export const useSelector: TypedUseSelectorHook<RootState> = _useSelector;
// μ€ν μ΄ λ΄λ³΄λ΄κΈ°
export default store;- μΆμ² 1 : Redux-Toolkit ννμ΄μ§
- μΆμ² 2 : Velog κΈ
