본문 바로가기
React-study/dil

[DIL] useReducer(1)

by 어느새벽 2024. 5. 27.

useReducer(reducer, initialArg, init?)

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

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

컴포넌트의 최상위 레벨에서 useReducer를 호출하여 reducer를 통해 state를 관리 할 수 있다.

import { useReducer } from 'react';

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

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

 

매개변수( Parameters )

  • reducer : state가 업데이트되는 방식을 지정하는 reducer 함수이다. 순수 함수여야 하고 state와 액션을 인자로 받아야 하며, 다음 state를 반환해야 한다. state와 액션은 어떤 유형이든 가능하다.
  • initialArg : 초기 state가 계산되는 값으로 모든 유형이 가능하다. 이 값에서 초기 state를 계산하는 방법은 다음 init 인자에 따라 달라진다.
  • 선택적(optional) init  : 초기 state 계산 방법을 지정하는 초기화 함수다. 미리 지정하지 않으면 초기 state는 initialArg로 설정되고 그렇지 않으면 초기 state는 init(initialArg)를 호출한 결과로 설정된다.

반환값( Returns )

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

주의사항( Caveats )

  • useReducer는 훅이므로 컴포넌트의 최상위 레벨 또는 자체 훅에서만 호출할 수 있다. 반복문이나 조건문 내부에서는 호출 할 수 없다. 필요하다면 새 컴포넌트를 추출하고 state를 그 안에 옮겨야 한다.
  • Strict Mode에서 React는 의도치 않은 불순물을 찾기 위해 reducer와 초기화 함수를 두 번 호출한다. 이는 개발 전용 동작이며 상용 환경에서는 영향을 미치지 않는다. reducer와 초기화 함수가 순수하다면(그래야 함) 컴포넌트의 로직에 영향을 미치지 않는다. 호출 중 하나의 결과는 무시된다.

dispatch function 

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

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

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

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

 

매개변수 ( Parameters )

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

 

반환값( Returns )

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

 

주의사항( Caveats )

  • dispatch함수는 다음 렌더링에 대한 state 변수만 업데이트 한다. 만약 dispatch 함수를 호출한 후 state변수를 읽으면 호출 전 화면에 있던 이전 값이 계속 표시된다.
  • 만약 여러분 제공한 새 값이 object.is로 비교했을 때 현재 state와 동일하다면, React는 컴포넌트와 그 자식들을 다시 렌더링하는 것을 건너뛴다(최적화). React는 결과를 무시하기 전에 여전히 컴포넌트를 호출하게 될 수도 있지만, 코드에 영향을 미치지는 않는다. 
  • React는 state업데이트를 일괄 처리한다. 모든 이벤트 핸들러가 실행되고 set 함수를 호출한 후에 화면을 업데이트 한다. 이렇게 하면 단일 이벤트 중에 여러번 다시 렌더링되는 것을 방지할 수 있다. 드물지만 DOM에 접근하기 위해 React가 화면을 더 일찍 업데이트하도록 강제해야 하는 경우, flushSync를 사용할 수 있다.
    // flushSync를 사용하여 count 상태를 즉시 업데이트
    flushSync(() => {
      setCount(count + 1);
    });

사용법( Usage )

컴포넌트에 reducer 추가하기 

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

import { useReducer } from 'react';

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

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

useReducer는 정확히 두 개의 항목이 있는 배열을 반환한다.

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

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

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

React는 현재 state와 액션을 reducer함수에 전달한다. Reducer는 다음 state를 계산하고 반환한다. React는 다음 state를 저장하고, 컴포넌트를 렌더링하고, UI를 업데이트한다. 아래 예시문 참고!

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

useReducer는 useState와 매우 유사하지만 이벤트 핸들러의 state 업데이트 로직을 컴포넌트 외부의 단일 함수로 옮길 수 있다. 더 자세한 내용 useState와 useReducer 중 하나를 선택하는 방법에서 확인할 수 있다.

 

reducer 함수 작성하기 

Reducer 함수는 다음과 같이 선언된다.

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

다음 state를 계산하고 반환할 코드를 입력해야 한다. 관례상 switch문으로 작성하는 것이 일반적이다.

swich의 각 case에 대해 다음 state를 계산하고 반환해야 한다.

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

액션 유형 이름은 컴포넌트에 로컬로 지정된다. 각 액션은 아무리 많은 데이터를 변경하게 되더라도 오직 하나의 상호작용만을 기술한다. state의 모양은 임의적이지만 일반적으로 객체나 배열이 된다.

 

state는 읽기 전용으로 state의 객체나 배열을 수정하면 안된다!

function reducer(state, action) {
  switch (action.type) {
    case 'incremented_age': {
      // 🚩 Don't mutate an object in state like this:
      state.age = state.age + 1;
      return state;
    }

대신, reducer로부터 새로운 객체를 반환한다.

function reducer(state, action) {
  switch (action.type) {
    case 'incremented_age': {
      // ✅ Instead, return a new object
      return {
        ...state,
        age: state.age + 1
      };
    }

 

'React-study > dil' 카테고리의 다른 글

[DIL] useContext  (0) 2024.05.29
[DIL] useReducer(2)  (0) 2024.05.28
[DIL] useState 사용법  (0) 2024.05.20
[DIL] useState(2)  (0) 2024.05.17
[DIL] useState(1)  (0) 2024.05.16