Store 만들기
src/stores 폴더 내부에 폴더 생성
createSlice
API 를 사용해서 action과 reducer 로직 정의→ slice 란, App 의 단일 기능에 대한 상태와, 액션 로직을 한 곳에 모아둔 것
import { createSlice } from '@reduxjs/toolkit'; export interface AuthState { token: string; } const initialState: AuthState = { token: '', }; export const authSlice = createSlice({ name: 'auth', initialState, reducers: { setAuth: (state, action) => { const { token } = action.payload; state.token = token; }, }, });
slice
를 만들고 나면, 해당 폴더의 index.ts
에서 reducer 와 action 을 export
해주고,stores/index.ts
의 combineReducers
내부에 Reducer
를 추가해준다.import { authSlice } from '@/stores/auth/slice'; import * as selector from '@/stores/auth/selector'; export const authReducer = authSlice.reducer; export const { setAuth } = authSlice.actions;
import { combineReducers } from '@reduxjs/toolkit'; import { authReducer } from '@/stores/auth'; import { layoutReducer } from '@/stores/layout'; // 여기에 reducer 를 추가해주세요 export const reducers = combineReducers({ auth: authReducer });
dispatch
를 통해 reducer
을 실행할 때는 useAppDispatch
를 사용한다const dispatch = useAppDispatch(); try { const rs: LoginResponseType = await login(loginRequest); const { token } = rs; dispatch(setAuth({ token })); } catch (error) { console.error(error); }
Selector 만들기
state 를 가져오기 위해서는
selector
를 정의해주어야 한다.slice
에 정의한 state
들을 가져올 수도 있고, 어떤 값이 바뀌었을 때 재계산되어야 하는 로직이 필요한 경우 createSelector
를 사용해 selector
를 정의할 수도 있다.import { createSelector } from '@reduxjs/toolkit'; import { RootState } from '@/stores'; import { AuthState } from '@/stores/auth/slice'; export const authSelector = (state: RootState): AuthState => state.auth; export const tokenSelector = (state: RootState): string => state.auth.token; export const isLoginSelector = createSelector(tokenSelector, (token: string) => Boolean(token), );
selector
를 만들고 나면, 해당 폴더의 index.ts
에서 export
해준다.import { authSlice } from '@/stores/auth/slice'; import * as selector from '@/stores/auth/selector'; export const authReducer = authSlice.reducer; export const { setAuth } = authSlice.actions; export const { authSelector, tokenSelector, isLoginSelector } = selector;
selector
로 값을 가져올 때는 다음과 같이 useAppSelector
를 사용한다import { useAppSelector } from '@/stores/hooks'; import { isLoginSelector } from '@/stores/auth/selector'; const isLogin = useAppSelector(isLoginSelector);
listener 만들기
값을 단순 계산하는 로직이 아니라, React 의
useEffect
hook 처럼, action
이 수행될 때 특정한 로직을 수행해야하는 경우라면, listener
를 만들어 사용한다.import { Unsubscribe } from '@reduxjs/toolkit'; import { AppStartListening } from '@/stores'; import { setAuth } from '@/stores/auth'; import session from '@/utils/sessionStorage'; import SESSION_STORAGE from '@/consts/sessionStorage'; // setAuth action 이 수행되면, token 을 sessionStorage 에 저장하는 effect 를 정의 const onUpdateToken = ({ payload }: ReturnType<typeof setAuth>) => { const { token } = payload; session.setItem(SESSION_STORAGE.TOKEN, token); }; // subscriptions 배열 안에 해당 action 을 startListening() 하고 난 반환값 저장 export const setupAuthListeners = ( startListening: AppStartListening, ): Unsubscribe => { const subscriptions = [ startListening({ actionCreator: setAuth, effect: onUpdateToken, }), ]; return () => { subscriptions.forEach((unsubscribe) => unsubscribe()); }; };
setAuth action 이 수행되면, token 을 sessionStorage 에 저장하는 effect 를 정의
const onUpdateToken = ({ payload }: ReturnType<typeof setAuth>) => { const { token } = payload; session.setItem(SESSION_STORAGE.TOKEN, token); };
Unsubscribe
함수를 반환하는 setupAuthListeners
함수를 만들어준다.
export const setupAuthListeners = ( startListening: AppStartListening, ): Unsubscribe => { const subscriptions = []; return () => { subscriptions.forEach((unsubscribe) => unsubscribe()); }; };
subscriptions
배열 안에 해당 action 을 startListening()
하고 난 반환값 저장const subscriptions = [ startListening({ actionCreator: setAuth, effect: onUpdateToken, }), ];
stores/listeners.ts
의 listeners 배열에 setupAuthListeners 를 추가import { Unsubscribe } from '@reduxjs/toolkit'; import { startAppListening } from '@/stores'; import { setupAuthListeners } from '@/stores/auth/listener'; // 이 listeners 배열에 추가해주시면 됩니다 const listeners = [setupAuthListeners]; export const setupListeners = (): Unsubscribe[] => { return listeners.map((setupListener) => setupListener(startAppListening)); };