개발일지

useReducer 본문

리액트 공식문서 스터디

useReducer

박수미/ 2024. 5. 22. 23:27

📌 useReducer란?

useReducer는 컴포넌트에 reducer를 추가할 수 있는 React Hook입니다.

const [state, dispatch] = useReducer(reducer, initialArg, init?)

 

📌 useReducer(reducer, initialArg, init?)

 

컴포넌트의 최상위 레벨에서 useReducer을 호출하여 reducer을 통해 state를 관리하세요.

import { useReducer } from 'react';

function reducer(state, action) {
  // ...
}

function MyComponent() {
  const [state, dispatch] = useReducer(reducer, { age: 42 });
  // ...

 

📍 매개변수

  • reducer: state가 업데이트되는 방식을 지정하는 reducer 함수입니다. 순수 함수여야 하며, state와 액션을 인자로 받아야 하고, 다음 state를 반환해야 합니다. state와 액션은 어떤 유형이든 가능합니다.
  • initialArg: 초기 state가 계산되는 값입니다. 모든 유형의 값일 수 있습니다. 이 값에서 초기 state를 계산하는 방법은 init인자에 따라 달라집니다.
  • optional inti: 초기 state 계산방법을 지정하는 초기화 함수입니다. 이것을 지정하지 않으면 초기 state는 initialArg로 설정됩니다. 그렇지 않으면 초기 state는 init을 호출한 결과로 설정됩니다.

📍 반환값

useReducer은 정확히 두 개의 값을 가진 배열을 반환합니다.

  1.  현재 state. 첫 번째 렌더링 중에는 init 또는 init이 없는 경우로 설정됩니다.
  2. state를 다른 값으로 업데이트하고 리렌더링을 촉발할 수 있는 dispatch function.

dispatch function

useReducer가 반환하는 dispatch함수를 사용하면 stat를 다른 값으로 업데이트하고 다시 렌더링을 촉발할 수 있습니다. dispatch함수에 유일한 인수로 액션을 전달해야 합니다:

const [state, dispatch] = useReducer(reducer, { age: 42 });

function handleClick() {
  dispatch({ type: 'incremented_age' });
  // ...

React는 reducer함수에 현재 state와 dispatch 한 액션을 전달하고, 그 결과를 다음 state로 설정합니다.

 

📍 매개변수

  • action: 사용자가 수행한 작업입니다. 어떤 데이터 유형이든 올 수 있습니다. 관용적으로 액션은 보통 이를 식별하는 type 속성이 있는 객체이며, 선택적으로 추가 정보가 있는 다른 속성을 포함할 수 있습니다.

📍 반환값

dispatch 함수에는 반환값이 없습니다.


📌 사용방법

1️⃣ 컴포넌트에 reducer 추가하기

컴포넌트의 최상위 레벨에서 useReducer을 호출하여 reducer로 state를 관리하세요.

import { useReducer } from 'react';

function reducer(state, action) {
  // ...
}

function MyComponent() {
  const [state, dispatch] = useReducer(reducer, { age: 42 });
  // ...

 

userReducer은 정확히 두 개의 항목이 있는 배열을 반환합니다:

  1. 이 state 변수의 현재 state 처음에 제공한 초기 state로 설정됨.
  2. 상호작용에 반응하여 이를 변경할 수 있는 dispatch 함수

화면에 표시되는 내용을 업데이트하려면 사용자가 수행한 작업을 나타내는 객체, 즉, 액션을 사용하여 dispatch를 호출합니다:

function handleClick() {
  dispatch({ type: 'incremented_age' });
}

React는 현재 state와 액션을 reducer 함수에 전달합니다. Reducer은 다음 state를 계산하고 반환합니다.

React는 다음 state를 저장하고, 컴포넌트를 렌더링 하고, UI를 업데이트합니다.

 

같은 counter을 높이는 기능을 만들었을 때 useState와 useReducer의 차이점을 알아보겠습니다.

useState

import { useState } from "react";

function Counter() {
  const [number, setNumber] = useState(0);

  const PlusBtn = () => {
    setNumber(number + 1);
  };

  const MinusBtn = () => {
    setNumber(number - 1);
  };

  return (
    <div>
      <h1>{number}</h1>
      <button onClick={PlusBtn}>+1</button>
      <button onClick={MinusBtn}>-1</button>
    </div>
  );
}

export default Counter;

 

useReducer

import { useReducer } from "react";

function reducer(state, action) {
  switch (action.type) {
    case "INCREMENT":
      return state + 1;
    case "DECREMENT":
      return state - 1;
    default:
      return state;
  }
}

function Counter() {
  const [number, dispatch] = useReducer(reducer, 0);

  const onIncrease = () => {
    dispatch({ type: "INCREMENT" });
  };

  const onDecrease = () => {
    dispatch({ type: "DECREMENT" });
  };

  return (
    <div>
      <h1>{number}</h1>
      <button onClick={onIncrease}>+1</button>
      <button onClick={onDecrease}>-1</button>
    </div>
  );
}

export default Counter;

useReducer과 useState는 매우 유사하지만 이벤트 핸들러의 state 업데이트 로직을 컴포넌트 외부의 단일 함수로 옮길 수 있습니다.

 

2️⃣ reducer 함수 작성하기

Reducer 함수는 다음과 같이 선언됩니다:

function reducer(state, action) {
  // ...
}

 

그런 다음 state를 계산하고 반환할 코드를 입력해야 합니다. 관례상 switch문으로 작성하는 것이 일반적입니다. switch의 각 case에 대해 다음 state를 계산하고 반환해야 합니다.

* 꼭 switch문으로만 작성해야 하는 것은 아니다.

function reducer(state, action) {
  switch (action.type) {
    case 'incremented_age': {
      return {
        name: state.name,
        age: state.age + 1
      };
    }
    case 'changed_name': {
      return {
        name: action.nextName,
        age: state.age
      };
    }
  }
  throw Error('Unknown action: ' + action.type);
}

 

액션은 어떤 형태든 가질 수 있습니다. 관례상 액션을 식별하는 type 프로퍼티가 있는 객체를 전달하는 것이 일반적입니다. 여기에는 reducer가 다음 state를 계산하는데 필요한 최소한의 필수정보가 포함되어야 합니다.

function Form() {
  const [state, dispatch] = useReducer(reducer, { name: 'Taylor', age: 42 });
  
  function handleButtonClick() {
    dispatch({ type: 'incremented_age' });
  }

  function handleInputChange(e) {
    dispatch({
      type: 'changed_name',
      nextName: e.target.value
    });
  }
  // ...

 

3️⃣ 초기 state 재생성 방지하기

React는 초기 state를 한 번 저장하고 다음 렌더링에서 이를 무시합니다.

function createInitialState(username) {
  // ...
}

function TodoList({ username }) {
  const [state, dispatch] = useReducer(reducer, createInitialState(username));
  // ...

createInitialState(username)의 결과는 초기 렌더링에만 사용되지만, 이후의 모든 렌더링에서도 여전히 이 함수를 호출하게 됩니다. 이는 큰 배열을 만들거나 값비싼 계산을 수행하는 경우 낭비가 될 수 있습니다.

 

이 문제를 해결하려면 useReducer 세 번째 인수에 초기화 함수를 전달할 수 있습니다.

function createInitialState(username) {
  // ...
}

function TodoList({ username }) {
  const [state, dispatch] = useReducer(reducer, username, createInitialState);
  // ...

함수를 호출한 결과인 createInitialState()가 아니라 함수 자체인 createInitialState를 전달하고 있다는 점에 유의하세요. 이렇게 하면 초기화 후에는 초기 state가 다시 생성되지 않습니다.

위의 예에서 createInitialState는 username 인수를 받습니다. 초기화 함수가 초기 state를 계산하는데 아무런 정보가 필요하지 않은 경우, useReducer의 두 번째 인수로 null을 전달할 수 있습니다.

'리액트 공식문서 스터디' 카테고리의 다른 글

useRef  (0) 2024.05.30
useContext  (0) 2024.05.28
1회차 발표 useState, useReducer  (0) 2024.05.24
useState  (1) 2024.05.17
props  (0) 2024.05.13