본문 바로가기

React/공부공부

[React] Redux 라이브러리 (createReducer)

 

[ 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;