Redux의 기본적인 틀을 만들어보자!
import { configureStore } from "@reduxjs/toolkit";
export const store = configureStore({
reducer: {},
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
import { useDispatch, useSelector } from "react-redux";
import type { TypedUseSelectorHook } from "react-redux";
import type { RootState, AppDispatch } from "@/store";
export const useAppDispatch: () => AppDispatch = useDispatch;
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
const x : (a:number) => string = (x) => String(x)
const x : (a:number) => string
const count = useSelector((state: RootState) => state.counter.value)
const count = useAppSelector((state) => state.counter.value)
import { createSlice } from "@reduxjs/toolkit";
import type { PayloadAction } from "@reduxjs/toolkit";
export interface CounterState {
value: number;
}
const initialState: CounterState = {
value: 0,
};
export const counterSlice = createSlice({
name: "counter",
initialState,
reducers: {
increment: (state) => {
state.value += 1;
},
decrement: (state) => {
state.value -= 1;
},
incrementByAmount: (state, action: PayloadAction<number>) => {
state.value += action.payload;
},
},
});
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
export default counterSlice.reducer;
createSlice를 사용해서 초기값, reducer 등을 정의할 수 있다.
그 후 action으로 사용하기 위해서 counderSlice.actions를 export 하고, 스토어에서 사용하기 위한 counterSlice.reducer도 export 한다.
import { configureStore } from "@reduxjs/toolkit";
import counterReducer from "../reducer/test";
export const store = configureStore({
reducer: {
// 추가
counter: counterReducer,
},
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
import { Provider } from "react-redux";
import Router from "./pages/Router";
import { store } from "./store";
interface Props {}
const App = ({}: Props) => {
return (
<Provider store={store}>
<Router />
</Provider>
);
};
export default App;
interface MyKnownError {
errorMessage: string
}
// 통신 성공 시 가져오게 될 데이터의 타입
interface TodosAttributes {
id: number;
text: string;
completed: boolean
}
// 비동기 통신 구현
const fetchTodos = createAsyncThunk<
// 성공 시 리턴 타입
TodosAttributes[],
// input type. 아래 콜백함수에서 userId 인자가 input에 해당
number,
// ThunkApi 정의({dispatch?, state?, extra?, rejectValue?})
{ rejectValue: MyKnownError }
>('todos/fetchTodos', async(userId, thunkAPI) => {
try {
const {data} = await axios.get(`https://localhost:3000/todos/${userId}`);
return data;
} catch(e){
// rejectWithValue를 사용하여 에러 핸들링이 가능하다
return thunkAPI.rejectWithValue({ errorMessage: '알 수 없는 에러가 발생했습니다.' });
}
})
// ... //
const todosSlice = createSlice({
// ...
extraReducers: (builder) => {
builder
// 통신 중
.addCase(fetchTodos.pending, (state) => {
state.error = null;
state.loading = true;
})
// 통신 성공
.addCase(fetchTodos.fulfilled, (state, { payload }) => {
state.error = null;
state.loading = false;
state.todos = payload;
})
// 통신 에러
.addCase(fetchTodos.rejected, (state, { payload }) => {
state.error = payload;
state.loading = false;
});
},
import {unwrapResult} from '@reduxjs/toolkit';
// ... //
try{
const resultAction = await dispatch(fetchTodos(1));
const todos = unwrapResult(resultAction);
setTodos(todos);
} catch(e){
console.log(e)
}