useReducer 사용하기

Doum Kim·2020년 5월 14일
0

React - 기초

목록 보기
16/20
post-thumbnail

useReducer를 사용하면 컴포넌트의 상태 업데이트 로직을 컴포넌트에서 분리할 수 있다. 상태 업데이트 로직을 컴포넌트 바깥으로 빼거나 다른 파일에 작성해 불러와서 사용 가능하다.

간단하게 사용법을 보면 아래와 같다.

useReducer

import React ,{useReducer} from 'react';

const initialState = {
  //초기상태
}

const reducer = (state, action) => {
  switch(action.type) {
    case "액션 타입" : 
      return '새로운 상태 반환';
    default :
      return state;
  }
}

const Example = () => {
  const [state,dispatch] = useReducer(reducer,initialState);
  
  return (...)
}

export default Example;

이제 우리가 만들었던 App.js에 적용을 해보면 아래와 같다.

App.js

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

const countActiveUsers = (users) => {
  console.log('카운트');
  return users.filter((user) => user.active).length;
};

const initialState = {
  users: [
    {
      id: 1,
      username: 'Kim',
      age: 20,
      active: false,
    },
    {
      id: 2,
      username: 'Lee',
      age: '30',
      active: false,
    },
    {
      id: 3,
      username: 'Choi',
      age: '50',
      active: false,
    },
  ],
  inputs: {
    username: '',
    age: '',
  },
};

const 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, action.user],
      };
    case 'REMOVE_USER':
      return {
        ...state,
        users: state.users.filter((user) => {
          return user.id !== action.id;
        }),
      };
    case 'TOGGLE_USER':
      return {
        ...state,
        users: state.users.map((user) => {
          return user.id === action.id
            ? { ...user, active: !user.active }
          : user;
        }),
      };
    default:
      return state;
  }
};

export default function App() {
  const [state, dispatch] = useReducer(reducer, initialState);

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

  const nextId = useRef(4);

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

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

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

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

  const count = useMemo(() => countActiveUsers(users), [users]);

  return (
    <div>
      <CreateUser
        username={username}
        age={age}
        onChange={onInputChange}
        onCreate={onCreate}
        />
      <div>activeUser : {count}</div>
      <UserList users={users} onRemove={onRemove} onToggle={onToggle} />
    </div>
  );
}

만약 컴포넌트에서 관리하는 값이 여러개 또는 상태의 구조가 복잡해지면 useReducer를 선택하는게 좋다. 그게 아닌 단순한 구조라면 useState를 사용해도 무방하다.
위의 예제는 파일을 분리하지 않아 useReducer의 이점이 보이지 않지만 type, action등을 분리한다면 훨씬 더 유지보수가 용이한 형태로 코드를 작성할 수 있다.

0개의 댓글