성능 최적화를 위해 사용하는 useMemo()
함수를 사용해보자.
useMemo
의 첫번째 파라미터에는 어떻게 연산할지 정의하는 함수를 넣어주면 되고, 두번째 파라미터에는 deps배열을 넣어주면 되는데, 이 배열 안에 넣은 내용이 바뀌면, 우리가 등록한 함수를 호출해서 값을 연산해주고, 만약에 deps내용이 바뀌지 않았다면, 이전에 연산한 값을 재사용하게 됩니다.
useMemo()
와 매우 비슷해보이는데 무엇이 다를까?
크게 다르지는 않지만, 사용하는 경우가 조금 다르다. useCallback()
은 특정 함수를 새로만들지 않고 재사용하고 싶을때 사용합니다.
const onCreate = () => {
const user = {
id: nextId.current,
username,
email
};
setUsers(users.concat(user));
setInputs({
username: '',
email: ''
});
nextId.current += 1;
};
위의 코드를 보면(원본은 출처를 통해서 예제 전체 코드를 확인가능하다.) 이 함수들이 예제에서 컴포넌트가 리렌더링 될 때마다 새로 만들어진다. 함수를 선언하는 것 자체는 크게 리소스를 차지하지는 않지만, 함수를 필요할 때만 새로 만들고 재사용하는 것은 중요하다.
(users
와 setUsers
는 setState()
로 만들어진 것이다.)
아래에 useCallback()
을 적용한 코드를 보자
const onCreate = useCallback(() => {
const user = {
id: nextId.current,
username,
email
};
setUsers(users.concat(user));
setInputs({
username: '',
email: ''
});
nextId.current += 1;
}, [users, username, email]);
주의할 점은 함수 안에서 사용하는 state혹은 props가 있다면 꼭 deps
배열 안에 포함시켜야 한다. 위의 예시에는 [users, username, email]
이 포함되어 있는 것을 볼 수 있다. 만일값을 넣지 않게 되면 함수에서 사용하는 값들이 가장 최신의 값이라는 보장을 못하게 된다.
이 함수는 컴포넌트의 props 가 바뀌지 않은 경우에 리렌더링을 방지하여 리렌더링 성능 최적화를 해줄 수 있는 React.memo 라는 함수를 알아보도록 하자.
이 함수는 사용방법이 매우 간단한데, 리렌더링이필요한 시점에만 리렌더링 되도록 만들 수 있다.
위에서 만들었던 CreateUser 부분의 코드에 적용하는 것을 보자.
import React from 'react';
const CreateUser = ({ username, email, onChange, onCreate }) => {
return (
<div>
<input
name="username"
placeholder="계정명"
onChange={onChange}
value={username}
/>
<input
name="email"
placeholder="이메일"
onChange={onChange}
value={email}
/>
<button onClick={onCreate}>등록</button>
</div>
);
};
export default React.memo(CreateUser);
위처럼 따로 component로 분리하고 단순하게 React.memo()
로 감싸주면 된다.
그리고 이외에도 더 추가적으로 최적화를 하고 싶다면?
deps에 users
를 가지고 있는 요소들이 리렌더링 되는 것을 방지하기 위해서 할 수 있는 방법이 있지 않을까.
정답은 함수형 업데이트이다.
아래에 예시가 있다.
const onRemove = useCallback(id => {
setUsers(users.filter(user => user.id !== id));
},
[users]
);
const onRemove = useCallback(id => {
setUsers(users => users.filter(user => user.id !== id));
}, []);
setUsers()
안에 업데이트를 할때 함수형으로 값을 넣는 차이를 볼 수 있다. 이렇게 하면 callback()
함수의 파라미터에서 최신 users()
를 참조할 수 있기 때문에, deps에 users
를 넣지 않아도 된다.
이런 기능들을 사용해서 리액트를 최적화하면 컴포넌트의 성능을 개선할 수 있다. 그런데, 리렌더링을 막을 수 없는 경우에는 굳이 사용하지 않는게 맞다. 추가적으로는 React.memo
에서 두번째 파라미터에 propsAreEqual
이라는 함수를 사용해서 특정 값만 비교하는 것도 가능하다.
export default React.memo(
UserList,
(prevProps, nextProps) => prevProps.users === nextProps.users
);
하지만 이를 잘못 사용하면 버그들이 발생할 수 있다. 지금 위쪽에서 우리가 함수형 업데이트로의 전환으로 변경을 해줬는데, 위에처럼 users만 비교를 하게 되면 다른 함수에서 users를 참조하지 않으므로 심각한 오류가 발생할 수 있다.