React

React ) useReducer 리듀서

이수광 2024. 2. 1. 21:56

공식문서 설명문

 

useReducer는 현재 상태와 액션 개체를 매개변수로 받아와 새로운 상태를 반환해주는 함수이다.

리듀서로 상태를 관리하려면 구성 요소의 최상위에서 호출해야한다. ( 공식 문서 내용 )

import { useReducer } from 'react';

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

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

매개변수

  • reducer: 상태가 업데이트되는 방식을 지정하는 감속기 함수입니다.순수해야 하고 상태와 작업을 인수로 가져와야 하며 다음 상태를 반환해야 합니다. 상태 및 작업은 모든 유형이 될 수 있습니다.
  • initialArg: 초기 상태가 계산되는 값입니다. 모든 유형의 값이 될 수 있습니다. 초기 상태가 계산되는 방법은 다음 인수에 따라 다릅니다 init.
  • optional init : 초기 상태를 반환해야 하는 초기화 함수입니다. 지정하지 않으면 초기 상태로 설정됩니다 initialArg. 그렇지 않으면 초기 상태는 를 호출한 결과로 설정됩니다 init(initialArg).

useReducer는 정확히 2개의 값을 가진 배열을 반환한다.

  1. 현재 상태 Init ( initialArg ) 또는 initialArg ( 없는 경우 init ).
  2. 상태를 다른 값으로 업데이트하고 다시 렌더링을 일으키는 기능은 dispatch이다. 

dispatch

  1. dispatch에서 반환한 함수를 사용하면 useReducer상태를 다른 값으로 업데이트하고 재렌더링을 일으킨다.
  2. dispatch함수에는 반환 값이 없다.
const [state, dispatch] = useReducer(reducer, { age: 42 });

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

 

action : 사용자가 수행할 작업. 모든 유형의 값이 될 수 있다.

사용예시 )

import { useReducer } from 'react';

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

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

 

컴포넌트의 최상위에서 리듀서를 호출한다.

리듀서의 현재 상태는 우리가 정한 초기 상태로 설정된다. ( age : 42 )

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

 

상태를 업데이트 하려면 dispatch로 작업을 나타내는 type ( action ) 을 사용하여 호출한다.

React는 현재 상태와 작업을 전달하고 Reducer는 다음 상태를 계산하고 반환한다.

// App.js
import { useReducer } from 'react';

function reducer(state, action) {
  if (action.type === 'incremented_age') {
    return {
      age: state.age + 1
    };
  }
  throw Error('Unknown action.');
}

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

  return (
    <>
      <button onClick={() => {
        dispatch({ type: 'incremented_age' })
      }}>
        Increment age
      </button>
      <p>Hello! You are {state.age}.</p>
    </>
  );
}

Reducer 함수 작성

리듀서는 다음과 같이 선언한다.

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

 

그 다음 상태를 계산하고 반환할 코드를 채워야한다.

관례적으로 switch 문을 작성하는 것이 일반적이라고 한다.

각각의 case에 상태를 계산하고 반환할 기능을 작성한다.

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 규칙에 따라 작업을 식별하는 속성과 함께 개체를 전달하는게 일반적이다.

리듀서가 다음 상태를 계산하는데 필요한 최소한의 정보를 포함하자.

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
    });
  }
  // ...

 

데이터가 여러 번 변경되더라도 각 작업은 단일 상호 작용한다.

더 많은 정보를 원하면 개발 문서를 보자 : https://ko.react.dev/learn/extracting-state-logic-into-a-reducer

 

 

state 로직을 reducer로 작성하기 – React

The library for web and native user interfaces

ko.react.dev


useState를 이용해 컴포넌트 내에서 상태 변화 함수는 컴포넌트 내에 존재해야 한다.

( 상태를 업데이트 하기 위해 기존의 상태를 참조해야 하기 때문 )

위와 같이 한 컴포넌트에 상태 변화 로직들이 들어있다 data를 써야하기 때문에

 

컴포넌트 내에 코드가 길어지고 무거워 지는 건 좋지않다.

이렇게 길고 복잡한 상태 변화 로직을 컴포넌트 밖으로 분리하자.

UseReducer를 이용하면 상태 변화 로직들을 컴포넌트에서 분리할 수 있게 되어

컴포넌트를 가볍게 작성 가능하다.

 

 

useState를 이용하면 위와 같이 상태 변화 함수를

각각 Counter 컴포넌트 안에 작성해야하고 코드가 길어지고 무거워진다.

 

 

위처럼 useReducer를 이용하면 reducer상태 변화 함수를 컴포넌트 밖에 분리하여

쉽게 처리할 수 있게 만들 수 있다.

배열을 반환하고 비구조 할당을 통해 사용한다.

const Counter = () => {
//                                                    ┌state의 초기값
	const [count, dispatch] = useReducer(reducer, 1);
//              └state    └상태변화 action     └상태변화 처리
   ...
   
   }
   
// 상태와, 액션을 가져와 어떤 타입인지 case로 찾아 실행한다.
   
  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);
}

 

내가 보기 위해 열심히 작성함.