일반적인 컴포넌트 개발시에는 상태값(변수)을 관리하기 위해 라이프사이클이나 hooks을 사용한다.
이 경우 각각의 컴포넌트가 관리하는 변수값들이 소스파일 여기저기에 흩어져 있기 때문에 코드 유지보수에 좋지 않다.
컴포넌트의 상태 업데이트 관련 로직을 다른 파일로 분리시켜서 더욱 효율적으로 관리할 수 있다.
즉, 여러 개의 컴포넌트가 개별적으로 관리하는 상태값들을 하나의 소스에 모아 놓고 통합 관리하는 것이 목적.
컴포넌트끼리 상태를 공유해야 할 때도 여러 컴포넌트를 거치지 않고 손쉽게 상태 값을 전달하거나 업데이트할
수 있다.
Slice
Store
Component
미들웨어로 수행하는 처리들
전달받은 액션을 단순히 콘솔에 기록 전달받은 액션 정보를 기반으로 액션을 아예 취소 다른 종류의 액션을 추가로 디스패치 비동기 액션 처리 등
동작순서
[사용자 이벤트] ――▶ [액션함수 디스패치] ――▶ [미들웨어 자동실행] ――▶ [리듀서의 액션함수 실행됨] ――▶ [액션값이 갱신됨]
/src/store.js
// 폴더와 파일을 직접 생성해야 함.
import { configureStore } from '@reduxjs/toolkit';
const store = configureStore({
// 개발자가 직접 작성한 Slice 오브젝트들이 명시되어야 한다.
reducer: {
...
}
});
export default store;
/src/index.js
// 리덕스를 위한 참조 추가
/** 리덕스 구성을 위한 참조 */
import { Provider } from 'react-redux';
import store from './store';
// 렌더링 처리
// 렌더링 처리를 <Provider store={store}> 태그로 감싼다.
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
);
/src/slices/slice이름.js
동기처리인 경우 (Ajax 사용 안하는 경우)
reducers
import { createSlice } from '@reduxjs/toolkit'
const slice이름 = createSlice({
name: 'slice이름',
// 이 모듈이 관리하고자하는 상태값들을 명시
initialState: {
변수1: 100,
변수2: 200
},
// 상태값을 갱신하기 위한 함수들을 구현
// 컴포넌트에서 이 함수들을 호출할 때 전달되는 파라미터는 action.payload로 전달된
다.
// initialState와 동일한 구조의 JSON을 리턴한다.
reducers: {
액션함수1: (state, action) => {...state},
액션함수2: (state, action) => {...state}
}
});
// 액션함수들 내보내기
export const { 액션함수1, 액션함수2 } = slice이름.actions;
// 리듀서 객체 내보내기
export default slice이름.reducer;
비동기처리인 경우 (Ajax를 사용하는 경우)
extraReducers
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'
import axios from 'axios';
const API_URL = '...';
/** Ajax처리를 위한 미들웨어 함수 정의 */
export const 함수이름 = createAsyncThunk("slice이름/함수이름", async (payload, {
rejectWithValue }) => {
let result = null;
try {
result = await axios.get|post|put|delete(API_URL);
} catch (err) {
result = rejectWithValue(err.response);
}
return result;
});
const slice이름 = createSlice({
name: 'slice이름',
// 이 모듈이 관리하고자하는 상태값들을 명시
initialState: {
data: null,
loading: false,
error: null
},
// 상태값을 갱신하기 위한 함수들을 구현
// Ajax의 처리 과정에 따라 자동으로 실행된다.
extraReducers: {
// 로딩중임을 표시
[함수이름.pending]: (state, { payload }) => {
return { ...state, loading: true }
},
// 처리가 완료된 경우 - 미들웨어 함수가 정상적으로 리턴한 경우
[함수이름.fulfilled]: (state, { payload }) => {
return {
data: payload?.data,
loading: false,
error: null
}
},
// 처리에 실패한 경우 - 미들웨어 함수 안에서 예외가 발생하여 catch블록이 실행된 경우
[함수이름.rejected]: (state, { payload }) => {
return {
data: payload?.data,
loading: false,
error: {
code: payload?.status ? payload.status : 500,
message: payload?.statusText ? payload.statusText : 'Server Error'
}
}
}
},
});
export default Slice파일이름.reducer;
/src/slice/store.js
import { configureStore } from '@reduxjs/toolkit';
import slice이름 from './slices/slice이름';
const store = configureStore({
// 개발자가 직접 작성한 Slice 오브젝트들이 명시되어야 한다.
reducer: {
slice이름: slice이름,
...
},
// 비동기 미들웨어 추가 (Ajax처리가 필요한 경우만 설정)
middleware: (getDefaultMiddleware) => getDefaultMiddleware({serializableCheck:
false}),
});
export default store;
필요한 기능 참조하기
// 상태값을 로드하기 위한 hook과 action함수를 dispatch할 hook 참조
import { useSelector, useDispatch } from 'react-redux'
// Slice에 정의된 함수 참조
// - 동기처리인 경우에는 리듀서 내의 액션함수 참조
// - 비동기 처리인 경우에는 Slice 내의 미들웨어 함수 참조
import { 함수1, 함수2 } from '../slices/slice이름';
컴포넌트 내부에서 hook을 통해 필요한 Object 생성
// hook을 통해 slice가 관리하는 상태값 가져오기
const {변수1, 변수2} = useSelector((state) => state.slice이름);
// dispatch 함수 생성
const dispatch = useDispatch();
필요한 이벤트 핸들러 안에서 액션함수 디스패치하기
dispatch(함수1(파라미터));
dispatch(함수2(파라미터));