개발/프론트엔드 (JS & React)

[React Hooks] useReducer 사용해서 Todo list 구현하기

jungwon_ 2022. 9. 25. 21:16

useReducer는 state를 관리하는 또다른 훅이다.

 

하나의 state를 관리하는 useState와는 다르게 useReducer는 좀 더 복잡한 상태의 state를 관리할 수 있다.

 

useReducer 사용법은 아래와 같다.

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

 

 

공식 문서의 카운터 예시를 통해 더 자세히 알아보자.

const initialState = {count: 0};

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
    </>
  );
}

// 출처: https://reactjs.org/docs/hooks-reference.html#usereducer

reducer는 (currentState, action) 를 받아 newState를 리턴하는 함수이다.

 

이 action은 보통 { type: "type_name", payload: { value: "value" } } 이런식이라고 이해하면 된다.

 

reducer은 action의 타입에 따라 새로운 state를 리턴한다.

 

위 예제의 경우 type이 increment인 경우 count를 +1 하고 decrement인 경우 count를 -1 해서 새로운 state를 리턴하는 것을 볼 수 있다.


카운터 예제보다 조금 더 복잡한 예제를 살펴보기 위해 Todo List를 구현해보자.

 

Todo list를 위해 ADD, TOGGLE, DELETE 세가지 액션을 사용할 것이다.

 

우선 useReducer로 state와 dispatch를 정의한다.

const [state, dispatch] = useReducer(reducer, { todos: [] })

reducer 함수는 아래와 같을 것이다.

function reducer(state, action) {
  switch (action.type) {
    case ACTIONS.ADD:
      // do something to add
      return newState
    case ACTIONS.TOGGLE:
      // do something to toggle
      return newState
    case ACTIONS.DELETE:
      // do something to delete
      return newState
    default:
      return state
  }
}

그런 다음 Todo 컴포넌트에 dispatch를 넘겨준다.

function App() {
  ...
  return (
  	<div>
    ...
    {state.todos.map(todo => <Todo key={todo.id} todo={todo} dispatch={dispatch} />}}
    </div>
  )
}

function Todo({ todo, dispatch }) {
  return (
    <div>
      <button onClick={() => dispatch({ type: ACTIONS.TOGGLE, payload: { id: todo.id }})}>
      	toggle
      </button>
      <span>{todo.name}</span>
      <button onClick={() => dispatch({ type: ACTIONS.DELETE, payload: { id: todo.id }})}>
        delete
      </button>
    </div>
  )
}

하위 컴포넌트인 Todo 컴포넌트에 dispatch를 넘겨주면 handleToggle, handleDelete 등을 따로 넘겨줄 필요 없이 하나로 넘겨줄 수 있어서 편하다.

 

또한 payload를 통해 원하는 value를 reducer로 보낼 수 있는 것도 장점이다.


이 글에서는 간단한 두 예제를 이용해 useReducer 사용법을 알아봤다.

 

Todo list 전체 코드는 여기에서 확인할 수 있다.

 

+ useReducer로 todo list를 구현하는 아이디어와 global non changing variable인 ACTIONS를 사용하는 방법은 [2]에서 참고했다.


Reference

[1] Hooks API Reference - React [Doc]

[2] Learn useReducer in 20 Minutes [Youtube] by Web Dev Simplfied