리액트 useReducer로 state 관리하기

REASON·2022년 9월 22일
0

React

목록 보기
25/27
post-thumbnail

useReducer

useState처럼 state 생성, 관리를 할 수 있도록 도와준다.
state가 많고 복잡할 때 useReducer를 사용하면 코드를 깔끔하게 사용 및 유지보수에도 도움이 된다.

useReducer의 핵심 3가지

  • reducer
  • dispatch
  • action

useReducer를 사용하여 state를 업데이트 하기 위해서는 dispatch 함수를 사용하여 업데이트 시킬 수 있다.

dispatch는 어떤 state를 어떻게 바꿀지에 대한 내용만 적어주면 된다. dispatch에서 적어준 내용에 대한 처리는 reducer 함수에서 처리하도록 만들면 된다.

import React, {useReducer} from 'react';

useReducer를 사용하기 위해서는 먼저 useReducer를 불러와야 한다.

import React, { useReducer } from "react";

const reducer = (state, action) => {};

const initialState = {};

function App() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div>
      <h2>useReducer 사용해보기</h2>
    </div>
  );
}

export default App;

기본적인 형태는 위 코드와 같다. 함수나 state의 내부 등은 아직 작성하지 않은 상태이다.

reducer 함수 : state와 action을 인자로 받아서 dispatch가 보내온 내용에 따라 state를 변경 시켜주는 함수

initialState : useState 보다 많은 것을 담을 수 있는 state 보관함이라고 생각하면 된다. 여러개의 useState를 사용하지 않아도 initialState 하나만 있으면 충분하다.

const [state, dispatch] = useReducer(reducer, initialState);

useReducer를 사용할 때 기본 형태이다.
dispatch를 사용하면 reducer에서 처리해주고
state를 사용하면 initialState에 넣어놓은 state들에 접근할 수 있다.

const initialState = {
  name : '아이유',
  age : 30,
};

예를 들면, initialState에 위와 같은 state를 저장했다고 가정해보자.
이때 '아이유' 또는 30이라는 값을 가져오고 싶다면 state.name , state.age 와 같은 형태로 state에 접근할 수 있다.

<div>
  <h2>useReducer 사용해보기</h2>
  <p>{state.name}</p>
  <button onClick={onClick}>이름 변경</button>
</div>

reducer 함수 만들기

const reducer = (state, action) => {
  switch (action.type) {
    case "CHANGE_NAME":
      return {
        ...state,
        name: action.name
      };
    default:
      return state;
  }
};

reducer 함수를 직접 호출하는 일은 없다.
dispatch를 통해 간접적으로 사용하게 되기 때문이다.
그러므로 reducer 함수 안에는 dispatch 함수로 보낸 내용을 처리하도록 코드를 작성하면 된다.

dispatch 를 사용하면 reducer를 내가 부르지 않았음에도 알아서 실행된다고 볼 수도 있을 것 같다.

우선 '이름 변경' 버튼을 누르면 dispatch가 reducer에게 이름을 이렇게 변경해달라는 요청을 하도록 코드를 작성해야 한다.

dispatch 로 reducer에게 요청하기

const onClick = () => {
    dispatch({
      type: "CHANGE_NAME",
      name: "이지은"
    });
  };

이제 이 onClick 함수가 실행 될 때 dispatch에 적은 내용이 reducer에게 전달이 된다.
오브젝트 안에 type : "메시지" 처럼 reducer에서 처리할 때 필요한 내용들을 key : value로 작성해주면 된다.
지금은 state의 name을 변경하기 위해 name : "이지은" 도 함께 담았다.

보통 type : "CHANGE_NAME" 처럼 type : 대문자_작성 이 관례이다.

type을 작성해주는 이유는 reducer 함수에서 switch 문을 통해 이 type에 따라 처리할 내용을 구분지어야 하기 때문이다.
(달랑 사람 이름만 보내면 뭘 하고 싶은지 reducer가 알지 못할테니..)

이제 다시 reducer 함수로 돌아가보자.

const reducer = (state, action) => {
  switch (action.type) {
    case "CHANGE_NAME":
      return {
        ...state,
        name: action.name
      };
    default:
      return state;
  }
};

보통 switch-case 문을 많이 사용한다.
if-else 문으로도 가능하겠지만.

이전에 onClick이 실행될 때 type명으로 "CHANGE_NAME" 이라는 문자열을 보냈었다.
type로 보낸 것을 받아보려면 action.type 으로 접근할 수 있다.

앞에서도 설명했지만, 이 type을 기준으로 reducer가 어떤 명령(state를 어떻게 바꿀지)을 실행할지 결정한다.

case에 만약 dispatch에서 보낸 type이 "CHANGE_NAME" 이면 이렇게 state 변경해주세요~ 를 이제 여기서 작성해주면 된다.

여기서 중요한 것은 불변성 을 지켜야 된다는 점이다.
return 을 살펴보면 ...state 라는 것이 보일텐데 reducer 함수의 첫번째 인수로 들어오는 state(처음 initialState에 정의했던 것들)의 불변성을 지켜야 한다는 것이다.

그래서 귀찮지만 불변성 유지를 위해 스프레드 문법을 사용하여 기존 state의 불변성을 유지 시키면서 변경될 state만 바꿔주어야 한다.

지금은 name만 변경시키면 되므로 name : action.name 형태로 작성해주었다.

dispatch 함수로 보낼 때 name : "이지은" 을 함께 보냈기 때문에 마찬가지로 action.name 으로 reducer 함수가 받아볼 수 있는 것이다.

전체 코드

import React, { useReducer } from "react";

const reducer = (state, action) => {
  switch (action.type) {
    case "CHANGE_NAME":
      return {
        ...state,
        name: action.name
      };
    default:
      return state;
  }
};

const initialState = {
  name: "아이유",
  age: 30
};

function App() {
  const [state, dispatch] = useReducer(reducer, initialState);

  const onClick = () => {
    dispatch({
      type: "CHANGE_NAME",
      name: "이지은"
    });
  };

  return (
    <div>
      <h2>useReducer 사용해보기</h2>
      <p>{state.name}</p>
      <button onClick={onClick}>이름 변경</button>
    </div>
  );
}

export default App;

참고로 이 코드는 너무 간단한 예제라 이런건 useState로 처리하는 게 더 편하긴하다.

state가 많아지는 경우 useReducer 사용을 고민해보자.

리덕스 없이 useReducer 사용으로 리덕스를 대체할 수도 있다.
물론 리덕스는 동기, useReducer는 비동기적 처리라는 점만 빼면..!

[번역] React Hooks가 Redux를 대체할 수 있냐고 물어보지 마세요

위 글도 함께 읽어보면 좋을 것 같다. :)

0개의 댓글