※ 강의를 듣고 제가 이해한 내용을 바탕으로 정리한 것이라서 오류가 있을 수 있습니다.

 

React- 커스텀 HOOK 만들기

컴포넌트를 만들다보면 반복되는 로직이 자주 발생 하는데 그럴때 커스텀 HOOK을 만들어서 쉽게 재사용할  수 있다. 커스텀 훅을 만들때에는 use라는 키워드로 시작하는 파일을 만들고

그 안에 함수를 작성하면 되는데 그 안에서 useState, useEffect, useReducer, useCallback 등 Hooks 를 사용하여 원하는 기능을 구현해주고 컴포넌트에서 사용하고 싶은 값들을 반환해주면 된다.

useInputs.js

import { useState, useCallback } from 'react';

function useInputs(initialForm) {
    const [form, setForm] = useState(initialForm); // form이라는 상태를 만드는데 그 초기 값은 파라미터로 가져온 initialForm
    const onChange = useCallback(e => {
        const { name, value } = e.target;
        setForm(form => ({
            ...form,
            [name]: value
        }));
    }, []);
    const reset = useCallback( () => setForm(initialForm), [initialForm]); // form을 리셋하는 함수 setForm의 파라미터는 초기값을 받아온 것을 설정해주겠다.

    return [form, onChange, reset]; // 3가지를 반환, 객체 형태로 써줘도 된다.
};

export default useInputs;

위 코드는 input을 위해 만든 커스텀 훅이다.

 

 

App.js

import React, {useRef, useReducer, useMemo, useCallback} from 'react';
import UserList from './components/UserList';
import CreateUser from './components/CreateUser';
import useInputs from './components/useInputs'; // useInputs 불러옴

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


const initialState = {
    users: [
        {
            id: 1,
            username: 'progaramexplorer',
            email: 'progaramexplorer@naver.com',
            active: true
        },
        {
            id: 2,
            username: 'tester',
            email: 'tester@gmail.com',
            active: false
        },
        {
           id: 3,
           username: 'liz',
           email: 'liz@daum.net',
           active: false 
        }
    ]
}

function reducer(state, action) {
  switch(action.type) {
    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:
      throw new Error('Unhandeled action');
  }
}

function App() {
  const [state, dispatch] = useReducer(reducer, initialState);
  const [form, onChange, reset] = useInputs({ // form: 상태, onChange:이벤트, reset:초기화 함수, useInputs으로 대체
    username:'', // 초기값
    email:'',
  });
  const { username, email } = form;
  const nextId = useRef(4);
  const {users} = state;
  
  

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

  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에서 기존의 inputs을  useIputs으로 대체해주고 

새로운 항목을 추가 할 때 input 값을 초기화해야 하므로 데이터 등록 후 reset() 을 호출해줘야한다.

 

이렇게 커스텀 Hook 을 만들어서 사용하면 컴포넌트의 로직을 분리시켜서 필요 할 때 쉽게 재사용 할 수도 있다.

+ Recent posts