아 ....
창업대회한다고 너무 개발 공부를 오랫동안 안했다.. 다시 열중
컴포넌트 안에 반복되는 로직이 있으면 커스텀 Hooks 를 만들어서 반복 로직을 쉽게 사용 가능하다.(그냥 리팩토링을 위해 함수 만드는 느낌이다)
use + 머시기 로 훅 이름을 정의하고, 기존 리액트 훅들을 이용해 기능을 구현한 뒤, 컴포넌트에서 사용할 값을 반환하면 끝!
인풋을 받을 때 훅인 useInputs 를 만들어보자.
import { useState, useCallback } from 'react';
function useInputs(initialForm) {
const [form, setForm] = useState(initialForm);
// change
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;
use+ 뭐시기에 Inputs가 들어갔으니, CRUD에서 inputs 를 받는 부분을 만들면 되는 것이다.
inputs 는 create 에서 타겟인 부분의 name(이름 or 이메일), value 를 받아와서 input 원래의 폼을 수정하는 것이다. (useReducer에서 ChangeInput 부분)
그래서 reducer 에서 ChangeInput 부분을 없얘고, initialState 에도 inputs 관련은 모두 없얜다. input 관련은 모두 이 훅이 담당한다.
function useInputs(initialForm) {
const [form, setFrom] = useState(initialForm);
const onChange = useCallback(e => {
const {name, value} = e.target;
setForm(form=>({...form, [name]:value}));
}, []);
}
똑같이 onChange 구현해주면 된다. 함수형 업데이트 이용하여 재랜더링 방지 잘 해준다(배운거 잘 써먹기)
setForm(form=>({...form, [name]:value}));
이부분이 헷갈리긴 한데, 함수형 업데이트 시에 객체 리터럴 표현을 반환할 때는 괄호속에 넣어야 된다고 하네...
return이 생략되어 있는거라 그런것!
쉽게(?)쓰면
setForm(form=>function(){return {...form, [name]:value};}); 이다.
여러 법칙이 있구만(ES5 법칙이라고 함.)
const reset = useCallback(()=>setForm(initialForm),[initialForm]);
그리고 reset을 만들어야 하는데 그 이유는 create 시에 초기화 해줘야 해서 그러하다.
기존에는 reducer create_user 단에서 inputs를 초기화해주면서 핸들링 했지만
이제는 inputs 관련 핸들링을 모두 커스텀 훅에 넘겨줬기에 리셋까지 커스텀 훅에서 담당해야 한다!
return [form, onChange, reset];
그리고 필요한 친구들 리턴해주면 된다
const initialState = {
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
}
]
};
말했듯 initialState는 users 뿐이다. inputs 관련은 모두 저 너머로
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;
}
}
reducer도 그러하다. change 부분은 빠지고, createuser 에서도 inputs 초기화가 사라졌다.
function App() {
const [{ username, email }, onChange, reset] = useInputs({
username: '',
email: ''
});
~~
이 부분에서 useInputs 를 써준다.
리턴하는게 [form,onChange,reset] 이니까 그대로 받아먹어준다.
form 은 username, email 로 비구조화 할당으로 먹어주고, 나머지는 어레이 할당으로 컴포넌트에 필요한 변수들을 정의해준다.
const onCreate = useCallback(() => {
dispatch({
type: 'CREATE_USER',
user: {
id: nextId.current,
username,
email
}
});
reset();
nextId.current += 1;
}, [username, email, reset]);
그리고 마지막으로 reset 을 onCreate 안에 넣어줘서 reset 되게 해준다.
끝!
또왔다 요놈
아까는 useState 썼는데 이제 useReducer 사용하란다.
설계를 할 때
reducer에 action type 이 change, reset 두가지로 switch 되게 구성하고
onChange, reset 둘다 dispatch 형태로 바꿔주면 되겠다 설계한다.
const [state, dispatch] = useReducer(reducer, initialForm)
국룰 써주고
const onChange = useCallback(e=>{
const {name, value} = e.target;
dispatch({
type: "CHANGE_INPUT",
name,
value
});
},[])
onChange 함수는 타겟을 받아서, dispatch 할 때 type을 "CHANGE_INPUT" 으로 하고 필요한 name value 와 함께 보내준다.
const reset = useCallback(()=>{
dispatch({
type: "RESET_INPUT",
initialForm
})
},[initialForm])
reset 함수는 dispatch 할 때 type을 "RESET_INPUT"으로 하고, initialForm 자체를 전달해서 initialForm 으로 초기화되게 한다.
const reducer=(state,{type, name, value, initialForm})=>{
switch (type){
case 'CHANGE_INPUT':
return{
...state,[name]:value
};
case 'RESET_INPUT':
return initialForm;
}
}
reducer 구성은 이제 익숙할 것이다.
이러면 진짜 끝났다.
이제 inputs 관련은 모두 외부에서 처리해준다!