[ useCallback 이란 ? ]
특정 함수를 새로만들지 않고 재사용 하고싶을 때 사용
[ useCallback은 언제사용해야 할까? ]
함수를 선언하는 것 자체는 사실 메모리도, CPU 도 리소스를 많이 차지 하는 작업은 아니기 때문에 함수를 새로 선언한다고 해서 그 자체 만으로 큰 부하가 생길일은 없지만, 한번 만든 함수를 필요할때만 새로 만들고 재사용하는 것은 여전히 중요합니다.
그 이유는, 우리가 나중에 컴포넌트에서 props 가 바뀌지 않았으면 Virtual DOM 에 새로 렌더링하는 것 조차 하지 않고 컴포넌트의 결과물을 재사용 하는 최적화 작업을 할건데요, 이 작업을 하려면, 함수를 재사용하는것이 필수입니다.
[ useCallback 예시 ]
useCallback 은 이런식으로 사용합니다.
import React, {useRef, useState, useMemo, useCallback}from 'react';
// import React from 'react';
import './App.css'
// import InputSample2 from './InputSample2';
import UserList from './UserList';
import CreateUser from './CreateUser';
// []의 의미는 key값으로 쓰겠다는 의미입니다.
// [name] 이 username과 email 두가지의 경우가 있는데,
// onChange라는 하나의 함수로 여러값을 저장하기 위해서 사용한것으로 보여집니다.
// [e.target.name] : e.target.value 이런식으로 state에 저장을하면 input마다
// 다른 함수를 사용하지 않고 여러개 input값을 저장할 수 있습니다.
// username:e.target.value, email: e.target.value
// 이렇게 dynamic하게 key값이 들어갑니다^^
//useMemo를 사용하여 연산한 값 재사용
//App 컴포넌트에서 다음과 같이 countActiveUsers 라는 함수를 만들어서, active 값이 true 인 사용자의 수를 세어서 화면에 렌더링을 해보세요.
function countActiveUsers(users) {
console.log('활성 사용자 수를 세는중 ...');
return users.filter(user => user.active).length;
}
function App() {
const [Inputs, setInputs] = useState({
username : '',
email : '',
id : ''
});
const {username, email, id} = Inputs;
const onChange = useCallback(e => {
const {name, value} = e.target;
setInputs({
...Inputs,
[name] : value
});
},[Inputs]
);
const [users,setUsers] = useState([
{
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
},
]);
//다음값 id지정
const nextId = useRef(4);
//새롭게 등록할 때
// useCallback으로 수정한부분
const onCreate = useCallback(() => {
const user = {
id : nextId.current,
username : username,
email : email
}
setUsers([...users, user]);
setInputs({
username : '',
email : ''
})
nextId.current += 1;
},[users, username, email]);
//삭제하기위해서는 각 id를 알아야한다
// useCallback으로 수정한부분
const onRemove = useCallback(id => {
//user.id가 파라미터로 일치하지 않는 원소만 추출해서 새로운 배열을 만듬
// 즉 user.id 가 id인것만 제거하고 그 외에는 새로배열을 생성
setUsers(users.filter(user => user.id !== id));
},[users]);
//수정
// useCallback으로 수정한부분
const onToggle = useCallback(id => {
setUsers(
users.map(user => user.id === id ? {...user, active : !user.active} : user)
)
},[users]);
//업데이트
// useCallback으로 수정한부분
const onUpdate = useCallback(() => {
setUsers (
users.map(user => user.id === id ? {...user, username: username, email : email} : user)
);
setInputs({
username : '',
email : '',
id : ''
})
},[users,username,email,id])
// 활성 사용자 수를 세는건, users 에 변화가 있을때만 세야되는건데,
//input 값이 바뀔 때에도 컴포넌트가 리렌더링 되므로 이렇게 불필요할때에도 호출하여서 자원이 낭비되고 있습니다.
// 이러한 상황에는 useMemo 라는 Hook 함수를 사용하면 성능을 최적화 할 수 있습니다.
// Memo 는 "memoized" 를 의미하는데, 이는, 이전에 계산 한 값을 재사용한다는 의미를 가지고 있습니다.
// 한번 사용해볼까요?
const count = useMemo(() => countActiveUsers(users),[users]);
const onModify = (user) => {
setInputs({
username : user.username,
email : user.email,
id : user.id
})
};
return (
<>
<CreateUser
username = {username}
email = {email}
onChange = {onChange}
onCreate = {onCreate}
onUpdate = {onUpdate}
/>
<UserList users={users} onRemove={onRemove} onToggle={onToggle} onModify={onModify}/>
<div>활성 사용자 수 : {count}</div>
</>
);
// return (
// <InputSample2 />
// )
}
export default App;
JavaScript
복사
[ 주의 할 점]
함수 안에서 사용하는 상태 혹은 props 가 있다면 꼭, deps 배열안에 포함시켜야 된다는 것 입니다. 만약에 deps 배열 안에 함수에서 사용하는 값을 넣지 않게 된다면, 함수 내에서 해당 값들을 참조할때 가장 최신 값을 참조 할 것이라고 보장 할 수 없습니다. props 로 받아온 함수가 있다면, 이 또한 deps 에 넣어주어야 해요.
사실, useCallback 은 useMemo 를 기반으로 만들어졌습니다. 다만, 함수를 위해서 사용 할 때 더욱 편하게 해준 것 뿐이지요. 이런식으로도 표현 할 수 있습니다.