React
React - 커스텀 hook 만들기
Starters
2020. 9. 13. 19:18
1. useInputs.js 생성하고 input에 관한 함수 만들기
import { useState, useCallback } from 'react';
// initialForm 은 input form에서 관리할 초기값
function useInputs(initialForm) {
// form이라는 새로운 상태 선언 - 그 상태의 초기값은 파라미터로 가져온 initialForm
const [form, setForm] = useState(initialForm);
const onChange = useCallback(e => {
const {name, value} = e.target;
// form을 업데이트
setForm(form => ({ ...form, [name]: value}));
// 의존하는 다른 상태 없음 빈 - 배열 사용
}, []);
// 초기값을로 설정해주겠다는 뜻
const reset = useCallback(() => setForm(initialForm), [initialForm]);
// 이제 useInputs사용하면 초기값을 파라미터로 받아오고,
// onChange로 상태 관리
// reset으로 초기화
return [form, onChange, reset];
}
export default useInputs;
2. App.js 에 적용
1) initState 객체에서 inputs 부분 삭제
2) useInputs 컴포넌트 선언
import useInputs from './useInputs';
3) { username, email } 선언 부분, onChange()부분 삭제
4) useInput()선언하고, username과 email form에서 추출
const [form, onChange, reset] = useInputs({
username: '',
email: '',
});
const { username, email } = form;
5) reset은 onCreate할 때, 추출
const onCreate = useCallback(() => {
dispatch({
type: 'CREATE_USER',
user: {
id: nextId.current,
username,
email,
}
});
nextId.current += 1;
reset();
}, [username, email, reset]);
결과 코드
import React, { useRef, useReducer, useMemo, useCallback } from 'react';
import UserList from './UserList';
import CreateUser from './CreateUser';
import useInputs from './useInputs';
function countActiveUsers(users){
console.log('활성 사용자 수를 세는 중...');
return users.filter(user => user.active).length;
}
const initialState = {
users: [
{
id: 1,
username: 'starters',
email: 'public.starters@gmail.com',
active: true,
},
{
id: 2,
username: 'tester',
email: 'tester@gmail.com',
active: false,
},
{
id: 3,
username: 'jajajojo',
email: 'jajajojo@gmail.com',
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:
throw new Error('Unhandled action');
}
}
function App() {
const [state, dispatch] = useReducer(reducer, initialState);
const [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();
}, [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;
useState 대신 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: 'REMOVE',
})
}, []);
return [form, onChange, reset];
}
export default useInputs;
이전 코드
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: 'starters',
email: 'public.starters@gmail.com',
active: true,
},
{
id: 2,
username: 'tester',
email: 'tester@gmail.com',
active: false,
},
{
id: 3,
username: 'jajajojo',
email: 'jajajojo@gmail.com',
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:
throw new Error('Unhandled action');
}
}
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;