컴포넌트를 만들다 반복되는 로직이 있다면 이때 커스텀 훅을 만들어 사용하면 반복되는 로직을 쉽게 재사용 할 수 있다.
공식적인 컨벤션으로 반드시 use라는 키워드를 사용한다. ( ex. useFetch)
그 후 useState, useEffect, useReducer, useCallback 등 Hooks를 사용하여 원하는 기능을 구현해주고, 컴포넌트에서 사용하고 싶은 값들을 반환해주면 된다.
Hook이 갯수가 많을경우엔 대게 src 폴더에 hooks 폴더에 담아 정리한다.
input을 관리하는 코드는 다룰 때마다 비슷한 코드가 반복되어 useInpusts.js라는 파일을 생성해 커스텀 훅을 만들어보자.
// useInputs.js
import {useState, useCallback} from 'react';
function useInputs(initialState){
const [form, setForm]=useState(initialState);
//change
const onChange=useCallback(e=>{
const {name, value}=e.target;
setForm(form=>({...form,[name]:value}));
},[])
const reset=useCallback(()=>setForm(initialState),[initialState]);
return [form, onChange, reset];
}
export default useInputs;
이제 useInputs 훅을 App.js에서 사용해보자.
import React, { useRef, useReducer, useMemo, useCallback } from 'react';
import UserList from './UserList';
import CreateUser from './CreateUser';
import useInputs from './hooks/useInputs'; //💪
function countActiveUsers(users) {
return users.filter(user => user.active).length;
}
const initialState = {
users: [
{
id: 1,
username: 'velopert',
email: 'public.velopert@gmail.com',
active: true
},
{
id: 2,
username: 'tester',
email: 'tester@example.com',
active: false
}
]
};
function reducer(state, action) {
switch (action.type) {
case 'CREATE_USER':
return {
users: state.users.concat(action.user)
};
case 'TOGGLE_USER':
return {
users: state.users.map(user =>
user.id === action.id ? { ...user, active: !user.active } : user
)
};
case 'REMOVE_USER':
return {
users: state.users.filter(user => user.id !== action.id)
};
default:
return state;
}
}
function App() {
const [{ username, email }, onChange, reset] = useInputs({
username: '',
email: ''
}); //💪
const [state, dispatch] = useReducer(reducer, initialState);
const nextId = useRef(4);
const { users } = state;
const onCreate = useCallback(() => {
dispatch({
type: 'CREATE_USER',
user: {
id: nextId.current,
username,
email
}
});
reset();
nextId.current += 1;
}, [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;