이다닷

[React] Day 23 - 커스텀 Hooks 만들기 본문

React

[React] Day 23 - 커스텀 Hooks 만들기

이다닷 2023. 10. 2. 18:00

📌컴포넌트를 만들다보면, 반복되는 로직이 자주 발생한다. 예를 들어서 Input을 관리하는 코드는 관리할 때마다 비슷한 코드가 반복된다.

이러한 상황에서 커스텀 Hooks를 만들어서 반복되는 로직을 쉽게 사용할 수 있다.

1️⃣scr 디렉터리에 hooks라는 디렉터리를 만들고, useInputs.js라는 파일 추가.

 

useInputs.js 추가

import { useState, useCallback } from 'react';

function useInputs(initialForm) {
  const [form, setForm] = useState(initialForm);

  const onChange = useCallback(e => {
    const {name, value} = e.target;
    setForm(form => ({ ...form, [name] : value }));
  }, []);

  const reset = useCallback(() => setForm(initialForm), [initialForm]);
  return [form, onChange, reset];
}

export default useInputs;

 

➡️APP.js에서 우리가 만든 Hooks 이용해주기

    -> useReducer에서 사용하던 inputs를 없애고, 이에 관련된 작업을 useInputs로 대체해준다.

 

APP.js 변경

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

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

const initialState = {
  inputs: {
    username: '',
    email: ''
  },
  users: [
    {
      id: 1,
      username: 'velopert',
      email: 'public.velopert@gmail.com',
      active: true
    },
    {
      id: 2,
      username: 'tester',
      email: 'tester@example.com',
      active: false
    },
    {
      id: 3,
      username: 'liz',
      email: 'liz@example.com',
      active: false
    }
  ]
};

// onChange, onCreate, onToggle, onRemove 함수를 위한 reducer 함수 생성.
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;

  // 원래 있던 inputs를 useInputs로 대체.
  // const { username, email } = state.inputs;
  const [{ username, email }, onChange, reset] = useInputs({
    username: '',
    email: ''
  });

  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;

 

➕useInputs.js를 useReducer을 사용해서 구현하기.

import { useReducer, useCallback } from 'react';

function reducer(state, action) {
  switch (action.type) {
    case 'CHANGE':
      return {
        ...state,
        [action.name]: action.value
      };
    case 'RESET':
      return Object.keys(state).reduce((acc, current) => {
        acc[current] = '';
        return acc;
      }, {});
    default:
      return state;
  }
}

function useInputs(initialForm) {
  const [form, dispatch] = useReducer(reducer, initialForm);
 
  const onChange = useCallback(e => {
    const { name, value } = e.target;
    dispatch({ type: 'CHANGE', name, value });
  }, []);
  const reset = useCallback(() => dispatch({ type: 'RESET' }), []);
  return [form, onChange, reset];
}

export default useInputs;