[React] 05.6 useReducer

27521 단어 ReactReact

useReducer

useReduceruseState보다 더 다양한 컴포넌트 상황에 따라 상태를 다른 값으로 업데이트 하고 싶을 때 사용한다. useReducer를 사용하여 상태 업데이트 로직을 컴포넌트에서 분리시킬 수 있어, 바깥이나 다른 파일에서 state를 업데이트 할 수 있다.

Reducer현재 상태, 액션(action: 업데이트를 위해 필요한 정보) 값을 전달받아 새로운 상태를 반환한다. 주의할 점은 새로운 상태를 만들 때 반드시 불변성을 지켜 주어야 한다는 것이다.

1. 액션 실행하기

useReducer를 사용해 Counter 컴포넌트를 구현해보자.

import React, { useReducer } from ‘react‘;

function reducer(state, action) {
  // action.type에 따라 다른 작업 수행
  switch (action.type) {
    caseINCREMENT:
      return { value: state.value + 1 };
    caseDECREMENT:
      return { value: state.value - 1 };
    default:
      // 아무것도 해당되지 않을 때 기존 상태 반환
      return state;
  }
}

const Counter = () => {
  const [state, dispatch] = useReducer(reducer, { value: 0 });

return (
    <div>
      <p>
        현재 카운터 값은 <b>{state.value}</b>입니다.
      </p>
      <button onClick={() => dispatch({ type:INCREMENT})}>+1</button>
      <button onClick={() => dispatch({ type:DECREMENT})}>-1</button>
    </div>
  );
};

export default Counter;

reducer의 첫 번째 파라미터에는 reducer 함수를 넣고, 두 번째 파라미터에는 해당 리듀서의 기본값을 넣어준다.여기서 state는 현재 가리키고 있는 상태고, dispatch액션을 발생시키는 함수이다.

const [state, dispatch] = useReducer(reducer, { value: 0 });

dispatch(action)과 같은 형태로, 함수 안에 파라미터로 액션 값을 넣어주면 reducer 함수가 호출되는 구조이다.

<button onClick={() => dispatch({ type:INCREMENT})}>+1</button>

2. 상태 관리하기

이번에는 useReducer를 사용하여 여러 상태값을 관리해보자

import React, { useRef, useReducer, useMemo, useCallback } from 'react';
import UserList from './UserList';
import CreateUser from './CreateUser';

function countActiveUsers(users) {
  console.log('활성 사용자 수를 세는중...');
  return users.filter(user => user.active).length;
}

const initialState = {
  inputs: {
    username: '',
    email: ''
  },
  users: [
    {
      id: 1,
      username: 'velopert',
      email: '[email protected]',
      active: true
    },
    {
      id: 2,
      username: 'tester',
      email: '[email protected]',
      active: false
    },
    {
      id: 3,
      username: 'liz',
      email: '[email protected]',
      active: false
    }
  ]
};

function reducer(state, action) {
  switch (action.type) {
    case 'CHANGE_INPUT':
      return {
        ...state,
        inputs: {
          ...state.inputs,
          [action.name]: action.value
        }
      };
    case 'CREATE_USER':
      return {
        inputs: initialState.inputs,
        users: state.users.concat(action.user)
      };
    case 'TOGGLE_USER':
      return {
        ...state,
        users: state.users.map(user =>
          user.id === action.id ? { ...user, active: !user.active } : user
        )
      };
    case 'REMOVE_USER':
      return {
        ...state,
        users: state.users.filter(user => user.id !== action.id)
      };
    default:
      return state;
  }
}

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

  const { users } = state;
  const { username, email } = state.inputs;

  const onChange = useCallback(e => {
    const { name, value } = e.target;
    dispatch({
      type: 'CHANGE_INPUT',
      name,
      value
    });
  }, []);

  const onCreate = useCallback(() => {
    dispatch({
      type: 'CREATE_USER',
      user: {
        id: nextId.current,
        username,
        email
      }
    });
    nextId.current += 1;
  }, [username, email]);

  const onToggle = useCallback(id => {
    dispatch({
      type: 'TOGGLE_USER',
      id
    });
  }, []);

  const onRemove = useCallback(id => {
    dispatch({
      type: 'REMOVE_USER',
      id
    });
  }, []);

  const count = useMemo(() => countActiveUsers(users), [users]);
  return (
    <>
      <CreateUser
        username={username}
        email={email}
        onChange={onChange}
        onCreate={onCreate}
      />
      <UserList users={users} onToggle={onToggle} onRemove={onRemove} />
      <div>활성사용자 수 : {count}</div>
    </>
  );
}

export default App;

이렇게 useReducer를 이용해서도 state 값을 관리하고 업데이트할 수 있다. 그러면 어떨 때 useReducer를 쓰고 useState를 써야 할까?

예를 들어 컴포넌트에서 관리하는 값이 하나고, 그 값이 단순한 데이터라면 useState로 관리하는 게 편할 것이다.

const [value, setValue] = useState(true);

하지만 만약 컴포넌트에서 관리하는 값이 여러개이고 상태의 구조가 복잡해진다면 useReducer로 관리하는 게 편해질 수도 있다. setter를 한 함수에서 여러번 사용해야 하는 일이 발생한다면 useReducer를 고민해볼 수 있을 것이다.

출처
https://thebook.io/080203/ch08/03/02/
https://react.vlpt.us/basic/20-useReducer.html

좋은 웹페이지 즐겨찾기