☝️ Flutter를 먼저 하면서 Provider, Bloc등을 사용해봤다면 이해가 빠르다
$ npx create-react-app redux_study --template typescript
$ code .
$ npm i @fontsource/material-icons
$ npm i -D postcss autoprefixer tailwindcss @tailwindcss/line-clamp daisyui
$ npm i redux @reduxjs/toolkit react-redux
/* ./src/index.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
.material-icons {
font-family: 'Material Icons';
display: inline-block;
}
// ./package.json
{
"name": "redux_study",
"version": "0.1.0",
"private": true,
"dependencies": {
...
"react-redux": "^8.0.5",
"react-scripts": "5.0.1",
"redux": "^4.2.1",
"typescript": "^4.9.5",
"web-vitals": "^2.1.4"
},
"scripts": {
...
},
"eslintConfig": {
...
},
"browserslist": {
...
},
"devDependencies": {
"@tailwindcss/line-clamp": "^0.4.4",
"autoprefixer": "^10.4.14",
"daisyui": "^2.51.5",
"postcss": "^8.4.21",
"tailwindcss": "^3.3.1"
}
}
configureStore
메서드를 사용하면 된다. const store = configureStore({ reducer: myReducerus });
// ./src/App.tsx
import {Provider as MyProvider} from 'react-redux';
// 구분하기 쉽도록 MyProvider라고 지정해두자
export default function App() {
{/* myReducer는 아직 없다. 새로 선언해야 한다 */}
const store = configureStore({ reducer: myReducer });
{/* 이 store는 임시, 따로 분리해서 새로 선언할 것이다 */}
return (
<MyProvider store={store}>
<main>
{/* 여기에 컴포넌트 추가^^ */}
</main>
</MyProvider>
);
}
./src/store/MyState.ts
export type MyState = {
today: Date;
};
./src/store/myActions.ts
import type { Action } from "redux";
export type SetTodayAction = Action<"setToday"> & {
today: Date;
};
export type MyActions = SetTodayAction;
./src/store/rootReducer.ts
import type { AppState } from "./AppState";
import type { MyActions } from "./myActions";
const initialAppState = {
today: new Date(),
};
export const rootReducer = (
state: AppState = initialAppState,
action: MyActions
) => {
switch (action.type) {
// Action에 적어둔 문자열로 구분
case "setToday": {
return { ...state, today: action.today };
}
}
return state;
};
이제 store를 파일을 나누어서 따로 선언한다. 보통 useMemo hook을 사용한다.
// ./src/store.useStore.ts
import { configureStore } from "@reduxjs/toolkit";
import { rootReducer } from "./rootReducer";
import { useMemo } from "react";
const initializeStore = () => {
const store = configureStore({ reducer: rootReducer });
return store;
};
export function useStore() {
const store = useMemo(() => initializeStore(), []);
return store;
}in
이렇게 하면 App.tsx의 프로바이더 컴포넌트에는 useStore()만 할당해주면 된다.
// ./src/App.tsx
import {Provider as MyProvider} from 'react-redux';
import { useStore } from "./store";
export default function App() {
const store = useStore();
return (
<MyProvider store={store}>
<main>
{/* 여기에 컴포넌트 추가^^ */}
</main>
</MyProvider>
);
}
store에 저장된 내용을 read하기 위해서는 useSelector훅을 사용한다.
useSelector훅은 리덕스에서 자체 제공한다.
const today = useSelector<MyState, Date>(state => state.today);
(현재 시간을 반환하는 useSelector hook)
시간을 표시하기 위한 컴포넌트를 짜서 App.tsx에 적용해보자
import { useSelector } from "react-redux";
import { AppState } from "../store";
import { Div, Subtitle, Title } from "../components";
export default function ReduxClock() {
const today = useSelector<AppState, Date>((state) => state.today);
return (
<Div className="flex flex-col item-center justify-center mt-16">
<Title className="text-5xl">Redux Clock</Title>
<Title className="mt-4 text-3xl">{today.toLocaleTimeString()}</Title>
<Subtitle className="mt-4 text-2xl">
{today.toLocaleDateString()}
</Subtitle>
</Div>
);
}
redux의 기본 제공 hook으로, useSelector가 state값을 read하기 위해서 사용되었다면,
이 hook은 state값을 변경하기 위해서 사용된다.
useDispatch hook이 state를 변경할 때에는 Action이 사용된다.
Action을 리덕스 store에 전달 할 때는 reducer를 통해서 가게 되는데,
위로 가서 reducer 선언문에 보면 switch 문으로 각 action이 전달되었을 때 동작을 정의해놓았다.
redux에서 제공해주는 useDispatch hook을 호출하면 바로 dispatch()가 사용이 가능하다.
const dispatch = useDispatch();
dispatch({ type: "$myActionType", myState: $newState });```
const dispatch = useDispatch();
const user = useSelector<UserState, UserData>(({ remoteUser }) => remoteUser);
const changeData = useCallback(() => {
toggleLoading();
() => dispatch(UserActions.changeData({...user, data:newData}))
.catch(setError)
.finally(toggleLoading);
}, [dispatch, toggleLoading]);
```