React
React - 컴포넌트 리렌더링 방지(React.memo)
Starters
2020. 9. 11. 13:29
컴포넌트의 리렌더링 성능 최적화
export default React.memo(UserList);
컴포넌트 내보낼 때 React.memo() 로 감싸주면 됨
CreateUsers도 마찬가지
-> UserList.js의 User 컴포넌트는 한 파일에 있는 또 다른 컴포넌트이기 때문에 함수 자체를 React.memo로 감싸준다
const User = React.memo(function User({ user, onRemove, onToggle }){
const {username, email, id, active} = user;
useEffect(() => {
console.log(user);
return () => {
console.log('user 값이 바뀌기 전');
console.log(user);
}
}, [user]);
return (
<div>
<b style={{
color: active ? 'green': 'black',
cursor: 'pointer'
}}
onClick={() => onToggle(id)}
>
{username}
</b>
<span>({email})</span>
<button onClick={() => onRemove(id)}>삭제</button>
</div>
);
});
- App.js -
App.js의 JSX 를 보면 onRemove() onToggle() 등이 바뀔 때, UserList.js의 내용과 User컴포넌트의 내용도 바뀌게 된다.
-> 입력할 때마다 모든 컴포넌트들 리렌더링 됨
-> 해결위해서 App.js의 onCreate(), onRemove(), onToggle() 함수가 기존 users를 참조하지 않게 해야한다.
=> useState의 함수형 업데이트 사용
const onCreate = useCallback(() => {
const user = {
id: nextId.current,
username,
email,
};
setUsers(users => users.concat(user));
setInputs({
username: '',
email: ''
});
console.log(nextId.current);
nextId.current += 1;
}, [username, email]);
이러면 deps에서도 users 쓸 필요 없이, username과 email이 바뀔 때만 onCreate함수 호출된다.
나머지도 동일한 방식으로
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
));
}, []);
React.memo의 두번째 파라미터 사용
- UserList.js -
export default React.memo(UserList, (prevProps, nextProps) => nextProps.users === prevProps.users);
이전 props와 다음 props가 같다면 리렌더링 하지 않겠다는 뜻
이전 코드
- 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,
[name]: value
});
}, [inputs]);
const [users, setUsers] = useState([
{
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,
}
]);
const nextId = useRef(4);
const onCreate = useCallback(() => {
const user = {
id: nextId.current,
username,
email,
};
setUsers(users.concat(user));
setInputs({
username: '',
email: ''
});
console.log(nextId.current);
nextId.current += 1;
}, [username, email, users]);
const onRemove = useCallback(id => {
setUsers(users.filter(user => user.id !== id))
}, [users]);
const onToggle = useCallback(id => {
setUsers(users.map(
user => user.id === id
? { ...user, active: !user.active }
: user
));
}, [users]);
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;
- CreateUser.js -
맨 아랫줄에 React.memo만 없음
- UserList.js -
import React, { useEffect } from 'react';
function User({ user, onRemove, onToggle }){
const {username, email, id, active} = user;
useEffect(() => {
console.log(user);
return () => {
console.log('user 값이 바뀌기 전');
console.log(user);
}
}, [user]);
return (
<div>
<b style={{
color: active ? 'green': 'black',
cursor: 'pointer'
}}
onClick={() => onToggle(id)}
>
{username}
</b>
<span>({email})</span>
<button onClick={() => onRemove(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 UserList;