- useMemo : 연산된 값을 재사용하기 위함
- useCallback : 특정함수를 재사용하기 위함
- React.memo : 렌더링 된 결과를 재사용하기 위함
1. useMemo
활성 사용자 수를 세는건,
users 에 변화가 있을때만 세야되는건데, input 값이 바뀔 때에도 컴포넌트가 리렌더링 되므로 이렇게 불필요할때에도 호출하여서 자원이 낭비되고 있다.
이를 해결하기 위해 useMemo를 사용한다.
- App.js
import React, { useRef, useState, useMemo } from 'react'; //useMemo
import UserList from './UserList';
import CreateUser from './CreateUser';
function countActiveUsers(users) { //countActiveUsers함수 생성
console.log('활성 사용자 수를 세는중...');
return users.filter(user => user.active).length;
}
function App() {
const [inputs, setInputs] = useState({
username: '',
email: ''
});
const { username, email } = inputs;
const onChange = e => {
const { name, value } = e.target;
setInputs({
...inputs,
[name]: value
});
};
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
}
]);
const nextId = useRef(4);
const onCreate = () => {
const user = {
id: nextId.current,
username,
email
};
setUsers(users.concat(user));
setInputs({
username: '',
email: ''
});
nextId.current += 1;
};
const onRemove = id => {
setUsers(users.filter(user => user.id !== id));
};
const onToggle = id => {
setUsers(
users.map(user =>
user.id === id ? { ...user, active: !user.active } : user
)
);
};
//첫번째 파라미터에는 어떻게 연산할지 정의하는 함수를 넣어주면 되고
// 두번째 파라미터에는 deps 배열을 넣어주면 되는데,
//이 배열 안에 넣은 내용이 바뀌면, 우리가 등록한 함수를 호출해서 값을 연산해주고, 만약에 내용이 바뀌지 않았다면 이전에 연산한 값을 재사용하게 됩니다.
const count = useMemo(() => countActiveUsers(users), [users]);
return (
<>
<CreateUser
username={username}
email={email}
onChange={onChange}
onCreate={onCreate}
/>
<UserList users={users} onRemove={onRemove} onToggle={onToggle} />
<div>활성사용자 수 : {count}</div>
</>
);
}
export default App;
2. useCallback
- useMemo 는 특정 결과값을 재사용 할 때 사용하는 반면,
useCallback 은 특정 함수를 새로 만들지 않고 재사용하고 싶을때 사용
- 컴포넌트가 리렌더링 될 때 마다 새로 만들어진다.
함수를 선언하는 것 자체는 사실 메모리도, CPU 도 리소스를 많이 차지 하는 작업은 아니기 때문에 함수를 새로 선언한 다고 해서 그 자체 만으로 큰 부하가 생길일은 없지만, 한번 만든 함수를 필요할때만 새로 만들고 재사용하는 것은 중요.
그 이유는, 우리가 나중에 컴포넌트에서 props 가 바뀌지 않았으면 Virtual DOM 에 새로 렌더링하는 것 조차 하지 않고
컴포넌트의 결과물을 재사용 하는 최적화 작업을 할건데, 이 작업을 하려면, 함수를 재사용하는것이 필수.
- App.js
import React, { useRef, useState, useMemo, useCallback } from 'react'; //useCallback
import UserList from './UserList';
import CreateUser from './CreateUser';
function countActiveUsers(users) {
console.log('활성 사용자 수를 세는중...');
return users.filter(user => user.active).length;
}
function App() {
const [inputs, setInputs] = useState({
username: '',
email: ''
});
const { username, email } = inputs;
const onChange = useCallback( //onChange에 useCallback 사용
e => {
const { name, value } = e.target;
setInputs({
...inputs,
[name]: value
});
},
[inputs]); ////onchange는 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
}
]);
const nextId = useRef(4);
const onCreate = useCallback(() => {
const user = {
id: nextId.current,
username,
email
};
setUsers(users.concat(user));
setInputs({
username: '',
email: ''
});
nextId.current += 1;
}, [users, username, email]);
const onRemove = useCallback( //onRemove에 useCallback사용
id => {
setUsers(users.filter(user => user.id !== id));
},
[users]); //onRemove시 id가 같지 않을때만 함수가 새로 만들어지고 그렇지 않으면 기존에 만든 함수를 재사용하게 된다
const onToggle = useCallback( //onToggle에 useCallback사용
id => {
setUsers(
users.map(user =>
user.id === id ? { ...user, active: !user.active } : user
)
);
},
[users]);//onToggle시 id가 같을 때만 함수가 새로 만들어지고 그렇지 않으면 기존에 만든 함수를 재사용하게 된다
const count = useMemo(() => countActiveUsers(users), [users]);
return (
<>
<CreateUser
username={username}
email={email}
onChange={onChange}
onCreate={onCreate}
/>
<UserList users={users} onRemove={onRemove} onToggle={onToggle} />
<div>활성사용자 수 : {count}</div>
</>
);
}
export default App;
3. React.memo
users 배열이 바뀔때마다 onCreate 도 새로 만들어지고, onToggle,onRemove 도 새로 만들어지기 때문에
User 중 하나라도 수정하면 모든 User 들이 리렌더링되고, CreateUser 도 리렌더링된다.
이는, 컴포넌트의 props 가 바뀌지 않았다면, 리렌더링을 방지하여 컴포넌트의 리렌더링 성능 최적화를 해줄 수 있다
- CreateUser.js
export default React.memo(CreateUser);
- UserList.js
특정 항목을 수정하게 될 때, 해당 항목만 리렌더링
import React from 'react';
const User = React.memo(function User({ user, onRemove, onToggle }) {
return (
<div>
<b
style={{
cursor: 'pointer',
color: user.active ? 'green' : 'black'
}}
onClick={() => onToggle(user.id)}
>
{user.username}
</b>
<span>({user.email})</span>
<button onClick={() => onRemove(user.id)}>삭제</button>
</div>
);
});
function UserList({ users, onRemove, onToggle }) {
return (
<div>
{users.map(user => (
<User
user={user}
key={user.id}
onRemove={onRemove}
onToggle={onToggle}
/>
))}
</div>
);
}
export default React.memo(
UserList,
(prevProps, nextProps) => prevProps.users === nextProps.users
);
- App.js
import React, { useRef, useState, useMemo, useCallback } from 'react';
import UserList from './UserList';
import CreateUser from './CreateUser';
function countActiveUsers(users) {
console.log('활성 사용자 수를 세는중...');
return users.filter(user => user.active).length;
}
function App() {
const [inputs, setInputs] = useState({
username: '',
email: ''
});
const { username, email } = inputs;
const onChange = useCallback(e => {
const { name, value } = e.target;
setInputs(inputs => ({
...inputs,
[name]: value
}));
}, []);
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
}
]);
const nextId = useRef(4);
const onCreate = useCallback(() => {
const user = {
id: nextId.current,
username,
email
};
setUsers(users => users.concat(user));
setInputs({
username: '',
email: ''
});
nextId.current += 1;
}, [username, email]);
const onRemove = useCallback(id => {
setUsers(users => users.filter(user => user.id !== id));
}, []);
const onToggle = useCallback(id => {
setUsers(users =>
users.map(user =>
user.id === id ? { ...user, active: !user.active } : user
)
);
}, []);
const count = useMemo(() => countActiveUsers(users), [users]);
return (
<>
<CreateUser
username={username}
email={email}
onChange={onChange}
onCreate={onCreate}
/>
<UserList users={users} onRemove={onRemove} onToggle={onToggle} />
<div>활성사용자 수 : {count}</div>
</>
);
}
export default App;
'FRONT' 카테고리의 다른 글
[React] userReducer (0) | 2023.07.20 |
---|---|
[React] useEffect를 사용하여 마운트/언마운트/업데이트시 할 작업 설정 (0) | 2023.07.19 |
[React] 배열에 항목 수정하기 (0) | 2023.07.18 |
[React] 배열에 항목 제거하기 (0) | 2023.07.18 |
[React] 배열에 항목 추가하기 (0) | 2023.07.18 |