[React] 리덕스 코드 작성하기

2021. 6. 9. 15:58Front-end/React

728x90
반응형

리덕스, react-redux 라이브러리 설치하기

yarn add redux react-redux

 

리덕스를 사용할 때 가장 많이 사용하는 패턴

리액트 프로젝트에서 리덕스를 사용할 때 가장 많이 사용하는 패턴은

프레젠테이셔널 컴포넌트 / 컨테이너 컴포넌트를 분리하는 것이다.

  • 프레젠테이셔널 컴포넌트 : 상태 관리가 이루어지지 않고, 그저 props를 받아와서 화면에 UI를 보여 주기만 하는 컴포넌트
  • 컨테이너 컴포넌트 : 리덕스와 연동되어 있는 컴포넌트, 리덕스로부터 상태를 받아 오기도 하고 리덕스 스토어에 액션을 디스패치 하기도 하는 컴포넌트

 

이러한 패턴은 필수 사항은 아니지만 이 패턴을 사용하면 코드의 재 사용성도 높아지고 관심사의 분리가 이루어져 UI를 작성할 때 좀 더 집중할 수 있다.

예제 만들어보기

import React from "react";
import Counter from "./components/Counter";
import Todos from "./components/Todos";
const App = (props) => {
  return (
    <>
      {" "}
      <Counter number={0} /> <hr /> <Todos />{" "}
    </>
  );
};
export default App;

✅App.js

 

import React from "react";
const Counter = ({ number, onIncrease, onDecrease }) => {
  return (
    <div>
      {" "}
      <h1>{number}</h1>{" "}
      <div>
        {" "}
        <button onClick={onIncrease}>+1</button>{" "}
        <button onClick={onDecrease}>-1</button>{" "}
      </div>{" "}
    </div>
  );
};
export default Counter;

 

✅Counter.js

 

import React from "react";
import TodoItem from "./TodoItem";
const Todos = ({
  input,
  todos,
  onChangeInput,
  onInsert,
  onToggle,
  onRemove,
}) => {
  const onSubmit = (event) => {
    event.preventDefault();
  };
  return (
    <>
      {" "}
      <form onSubmit={onSubmit}>
        {" "}
        <input /> <button type="submit">등록</button>{" "}
      </form>{" "}
      <div>
        {" "}
        <TodoItem /> <TodoItem /> <TodoItem /> <TodoItem /> <TodoItem />{" "}
      </div>{" "}
    </>
  );
};
export default Todos;

✅Todos.js

 

import React from "react";
const TodoItem = ({ todo, onToggle, onRemove }) => {
  return (
    <div>
      {" "}
      <input type="checkbox" /> <span>예제 텍스트</span> <button>삭제</button>{" "}
    </div>
  );
};
export default TodoItem;

✅TodoItem.js

 

리덕스 관련 코드 작성하는 방법

1. 액션 타입, 액션 생성 함수, 리듀서 코드를 각각 다른 파일에 작성하는 방법

actions

  > counter.js

  > todos.js

 

constants

  > ActionTypes.js

 

reducers

  > counter.js

  > todos.js

 

새로운 액션을 만들 때마다 세 종류의 파일을 모두 수정해야 하기 때문에 불편하기도 하다.

리덕스 공식 문서에서도 사용되므로 가장 기본적인 방법이지만 불편할 수도 있는 구조이다.

 

2. 액션 타입, 액션 생성 함수, 리듀서 함수를 파일 하나에 몰아서 작성하는 방법(Ducks 패턴)

modules

  > counter.js

  > todos.js

 

Ducks 패턴을 사용하여 액션 타입, 액션 생성 함수, 리듀서를 작성한 코드를 '모듈' 이라고 한다.

 

counter 모듈 작성하기

1. 액션 타입 정의하기

const INCREASE = "counter/INCREASE";
const DECREASE = "counter/DECREASE";

✅module/counter.js

액션 타입은 대문자로 정의하고 '모듈 이름/액션 이름' 형태로 작성한다.

모듈 이름을 넣음으로써 다른 모듈과의 액션 이름이 충돌 되지 않게 해준다.

2. 액션 생성 함수 만들기

const INCREASE = "counter/INCREASE";
const DECREASE = "counter/DECREASE";
export const increase = () => ({ type: INCREASE });
export const decrease = () => ({ type: DECREASE });

✅module/counter.js

액션 타입을 정의한 다음에는 액션 생성 함수를 만들어주어야 한다.

여기서 주의해야할 점은 export가 붙는다는 것이다. 이렇게 함으로써 추후 이 함수를 다른 파일에서 불러와서 사용할 수 있다.

 

3. 초기 상태와 리듀서 함수 만들기

const INCREASE = "counter/INCREASE";
const DECREASE = "counter/DECREASE";
export const increase = () => ({ type: INCREASE });
export const decrease = () => ({ type: DECREASE });
const initialState = { number: 0 };
const counter = (state = initialState, action) => {
  switch (action.type) {
    case INCREASE:
      return { number: state.number + 1 };
    case DECREASE:
      return { number: state.number - 1 };
    default:
      return state;
  }
};
export default counter;

초기 상태로 number에 0을 설정해주었고,

counter라는 리듀서 함수에는 현재 상태를 참조하여 새로운 객체를 생성해서 반환하는 코드를 작성해주었다.

  • 액션 생성 함수는 export로 내보내주었고 리듀서 함수는 export default로 내보내주었는데 차이점은 무엇인가?
    • export: 여러 개를 내보낼 수 있음
    • export default: 단 하나만 내보낼 수 있음

 

todos 모듈 정의하기

//1. 액션 타입 정의
const CHANGE_INPUT = "todos/CHANGE_INPUT";
const INSERT = "todos/INSERT";
const TOGGLE = "totos/TOGGLE";
const REMOVE = "todos/REMOVE";

//2. 액션 생성 함수 정의
export const changeInput = (input) => ({ type: CHANGE_INPUT, input });
let id = 3;
export const insert = (text) => ({
  type: INSERT,
  todo: { id: id++, text, done: false },
});
export const toggle = (id) => ({ type: TOGGLE, id });
export const remove = (id) => ({ type: REMOVE, id });

//3. 초기상태와 리듀서 함수 정의
const initialState = {
  input: "",
  todos: [
    { id: 1, text: "방 청소하기", done: true },
    { id: 2, text: "리덕스 공부 완료하기", done: false },
  ],
};
const todos = (state = initialState, action) => {
  switch (action.type) {
    case CHANGE_INPUT:
      return { ...state, input: action.input };
    case INSERT:
      return { ...state, todos: state.todos.concat(action.todo) };
    case TOGGLE:
      return {
        ...state,
        todos: state.todos.map((todo) => {
          if (todo.id === action.id) {
            return { ...todo, done: !todo.done };
          }
          return todo;
        }),
      };
    case REMOVE:
      return {
        ...state,
        todos: state.todos.filter((todo) => todo.id !== action.id),
      };
    default:
      return state;
  }
};
export default todos;

✅modules/todos.js

 

루트 리듀서 만들기

위의 module 파일에서 리듀서를 여러개 만들었는데, 나중에 createStore 함수를 사용하여 스토어를 만들 때는 리듀서를 하나만 사용해야 한다.

그러므로 기존에 만들었던 리듀서 함수들을 하나로 합쳐 주어야 한다.

→ 리덕스에서 제공하는 combineReducers 라는 유틸 함수를 사용하여 쉽게 처리할 수 있음

 

import { combineReducers } from "redux";
import counter from "./counter";
import todos from "./todos";
const rootReducer = combineReducers({ counter, todos });
export default rootReducer;

✅modules/index.js

728x90
반응형