[ Redux ]
애플리케이션의 전역 상태를 관리하기 위한 라이브러리.
Redux Toolkit(RTK)을 설치하면 Redux의 거의 모든 기능들을 사용 할 수 있음.
[ Redux 주요개념 ]
* Store : 전역 상태가 저장되는 곳. 하나의 애플리케이션에 단 하나의 스토어만 존재.
* Action : 상태를 변경하기 위한 명령. 객체 형태로 'type'필드를 반드시 포함.
* Dispatch : 액션을 스토어에 보내는 함수.
* Reducer : 액션이 발생했을 때 상태를 어떻게 변경할 지 정의하는 함수.
* Subscription : 스토어의 상태가 변경될 때 특정 함수가 호출되도록 설정하는 기능.
(데이터 흐름)
사용자와의 상호작용으로 액션 객체 생성 => 생성된 액션 디스패치 => 디스패치된 액션이 리듀서에 전달 => 리듀서는 현재 상태와 액션을 기반으로 새로운 상태를 계산 => 리듀서가 반환한 새 상태가 스토어에 저장. => 상태가 업데이트되면 스토어에 구독된 컴프넌트들이 이를 감지하고 리렌더링.
createReducer _ 리듀서와 액션 분리
createSlice _ 리듀서와 액션 통합 (reducer와 Action creator를 한 곳에서 정의. )
(스토어 설정)
* configureStore
RTK에서 제공, redux 스토어 설정을 위한 자동 설정 옵션들이 포함된 함수. (기존 redux의 createStore )
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './features/counter/counterSlice';
export const store = configureStore({
reducer: {
counter: counterReducer,
user: userReducer,
// 다른 리듀서를 추가
},
});
=> reducer : 리듀서 함수, 리듀서 객체 전달
( 다른 컴포넌트에서 각 리듀서 내부의 키값 'counter', 'user' 으로 상태 슬라이스에 접근)
=> 이외에 middleware, devTools, preloadedState 등 설정 가능.
단일 리듀서 사용시 아래처럼만 해줘도 된다.
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './features/counter/counterSlice';
const store = configureStore({
reducer: reducer, // 단일 리듀서 함수 전달
});
export default store;
ex1) counterReducer
(리듀서 설정)
* createReducer
RTK에서 제공, 상태를 업데이트 하는 리듀서를 작성하기 위한 함수.
import { createReducer } from '@reduxjs/toolkit';
import { increment, decrement, incrementByAmount } from './counterActions';
// 초기 상태
const initialState = { value: 0 };
// 리듀서 생성
const counterReducer = createReducer(initialState, {
[increment]: (state) => {
state.value += 1;
},
[decrement]: (state) => {
state.value -= 1;
},
[incrementByAmount]: (state, action) => {
state.value += action.payload;
},
});
export default counterReducer;
=> initialState : createReducer 함수의 첫번째 인자, 초기상태
=> 액션 핸들러 객체 : createReducer 함수의 두번째 인자, 객체
- 액션타입 (액션 크리에이터) : 객체의 key값 ( increment, devrement, incrementByAmount )
- 액션 핸들러 : 액션이 발생했을 때 실행될 함수.
ex) increment 액션이 발생하면 state에 저장된 value값이 +1 증가.
(액션 설정)
* createAction : 동기적 액션 생성
import { createAction } from '@reduxjs/toolkit';
// 동기적 액션 생성
export const increment = createAction('counter/increment');
// 생성된 액션 크리에이터를 사용하여 액션 객체 생성
const action = increment(1);
console.log(action);
// { type: 'counter/increment', payload: 1 }
ex2) userReducer
(리듀서 설정) _ userReducer
import { createReducer } from '@reduxjs/toolkit';
import { setUser, clearUser } from './userActions';
// 초기 상태
const initialState = { name: '', age: 0 };
// 리듀서 생성
const userReducer = createReducer(initialState, {
[setUser]: (state, action) => {
state.name = action.payload.name;
state.age = action.payload.age;
},
[clearUser]: (state) => {
state.name = '';
state.age = 0;
},
});
export default userReducer;
(액션 설정)
* createAction : 동기적 액션 생성
import { createAction } from '@reduxjs/toolkit';
// 액션 크리에이터
export const setUser = createAction('user/setUser');
( 액션 디스패치 )
dispatch(setUser({ name: 'Alice', age: 25 }));
ex3) userReducer
(리듀서 설정) _ userReducer
import { createReducer } from '@reduxjs/toolkit';
import { fetchUserById, setUser } from './userActions';
// 초기 상태
const initialState = {
user: null,
status: 'idle', // 'idle' | 'loading' | 'succeeded' | 'failed'
error: null,
};
// 리듀서 생성
const userReducer = createReducer(initialState, (builder) => {
builder
.addCase(fetchUserById.pending, (state) => {
state.status = 'loading';
state.error = null;
})
.addCase(fetchUserById.fulfilled, (state, action) => {
state.status = 'succeeded';
state.user = action.payload; // 성공 시 user 데이터 설정
state.error = null;
})
.addCase(fetchUserById.rejected, (state, action) => {
state.status = 'failed';
state.error = action.error.message; // 실패 시 에러 메시지 설정
alert(`Error: ${action.error.message}`); // 알림 표시
});
});
export default userReducer;
(액션 설정)
* createAsyncThunk : 비동기적 액션 생성 > API 호출, 작업 진행상태 관리 (로딩, 성공, 실패)
import { createAsyncThunk } from '@reduxjs/toolkit';
import axios from 'axios';
// 비동기 액션 생성
export const fetchUserById = createAsyncThunk(
'user/fetchById',
async (userId, thunkAPI) => {
const response = await axios.get(`/api/user/${userId}`);
return response.data;
}
);
// 사용 예시
dispatch(fetchUserById(1));
=> api호출 이후 pending, fulfilled, rejected 세 가지 상태가 자동으로 관리
(컴포넌트에서 활용)
import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { fetchUserById } from './userActions';
function UserProfile({ userId }) {
const dispatch = useDispatch();
const { user, status, error } = useSelector((state) => state.user);
useEffect(() => {
dispatch(fetchUserById(userId));
}, [dispatch, userId]);
if (status === 'loading') {
return <p>Loading...</p>;
}
if (status === 'failed') {
return <p>Error: {error}</p>;
}
if (status === 'succeeded' && user) {
return (
<div>
<h1>{user.name}</h1>
<p>Age: {user.age}</p>
<p>Email: {user.email}</p>
</div>
);
}
return null;
}
export default UserProfile;
'React > 공부공부' 카테고리의 다른 글
[React] Styled-Components 참고 (props warning / Transient Props) (0) | 2024.09.25 |
---|---|
[ React / ag-grid ] grid 내부에 버튼 넣기 (0) | 2024.08.21 |
[React Hook] UseMemo, UseCallback (0) | 2024.06.24 |
[React] Debounce 를 활용한 검색기능. (0) | 2024.06.22 |
[React] 리렌더링 조건 (0) | 2024.06.15 |