useMemo
Memoization
- 비용이 많이 드는 함수 호출의 결과를 저장하고 동일한 입력이 다시 발생할 때 캐시된 결과를 반환하여 컴퓨터 프로그램의 속도를 높이는데 주로 사용되는 최적화 기술
- 이것을 적절하게 활용하면 중복 연산을 피할 수 있기 때문에 메모리를 조금 더 쓰더라도 애플리케이션의 성능을 최적화 할 수 있음
function Component({a, b}) {
const result = compute(a, b)
return <div>{result}</div>
}
- Component 내의 computer 함수가 만약 복잡한 연산을 수행하면 결과 값을 리턴하는데 오랜 시간이 걸림
- 이럴 때 컴포넌트가 계속 리 렌더링 된다면 연산을 계속 수행하는데 오랜 시간이 걸려서 성능에 안 좋은 영향을 미치게 되고, UI 지연 현상도 일어나게 됨
- 이러한 현상을 해결해주기 위해서 useMemo를 사용
- compute 함수에 넘겨주는 a, b의 값이 이전과 동일하다면 컴포넌트가 리 렌더링 되더라도 연산을 다시 하지 않고 이전 렌더링 때 저장해두었던 값을 재활용하게 됨
- useMemo는 메모이제이션된 값을 반환하는 함수
useMemo(() => fn, [deps]);
/* deps로 지정한 값이 변하게 된다면 () => fn 함수를 실행하고, 그 함수의 반환 값을 반환해줌
deps는 dependency의 약어로 의존성을 뜻하며, useMemo가 이 deps에 의존하고 있다는 것을 말함 */
- useMemo 적용하기
// useMemo로 감싸준 후에 첫번째 인수에 의존성 배열에 compute 함수에서 사용하는 값을 넣어줌
function Component({a, b}) {
const result = useMemo(() => compute(a, b), [a, b])
return <div>{result}</div>
}
useCallback
- useCallback은 메모이제이션된 함수를 반환함
useCallback(fn, [deps]);
// useCallback 또한 deps, 의존성이 있는 값이 변하면 fn에 등록한 함수를 반환함
useMemo(() => console.log(), [test]);
const memoizedCallback = useCallback(() => console.log(), [test]);
// useCallback이 함수를 반환하기 때문에, 그 함수를 가지는 const 변수에 초기화 하는 것이 일반적임
자식 컴포넌트에 props로 함수를 전달하는 경우
- 컴포넌트에서 특정 함수를 정의할 경우 각각의 함수들은 모두 고유한 함수가 됨
- 이런 고유한 함수가 생성될 경우, 부모를 통해 props에 함수를 전달받는 자식 컴포넌트에서는 props가 변경되었다고 판단해 리렌더링이 발생하게 됨
function App() {
const [name, setName] = useState('');
const onSave = () => {};
return (
<div className="App">
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
<Profile onSave={onSave} />
</div>
);
}
- useCallback을 사용하지 않을 경우, name이 변경되어 리렌더링이 발생하면 onSave함수가 새로 만들어지고, Profile 컴포넌트의 props로 onSave함수가 새로 전달됨
- 이때, Profile 컴포넌트에서 useMemo를 사용해도 이전 onSave와 이후 onSave가 같은 값을 반환하지만 참조가 다른 함수가 되기 때문에 리렌더링이 발생
import React, { useCallback, useState } from 'react';
import Profile from './Profile';
function App() {
const [name, setName] = useState('');
const onSave = useCallback(() => {
console.log(name);
}, [name]);
return (
<div className="App">
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
<Profile onSave={onSave} />
</div>
);
};
- useCallback을 사용해서 onSave라는 함수를 재사용하는 것으로 자식 컴포넌트의 리렌더링을 방지할 수 있음
외부에서 값을 가져오는 api를 호출하는 경우
import React, { useState, useEffect } from "react";
function Profile({ userId }) {
const [user, setUser] = useState(null);
const fetchUser = () =>
fetch(`https://your-api.com/users/${userId}`)
.then((response) => response.json())
.then(({ user }) => user);
useEffect(() => {
fetchUser().then((user) => setUser(user));
}, [fetchUser]);
// ...
}
- fetchUser 함수가 변경될 때만 외부에서 api를 가져와 useEffect가 실행되어야 함
- Profile이라는 컴포넌트가 리렌더링이 발생할 경우 fetchUser 함수에는 새로운 함수가 할당됨
- useEffect()함수가 호출되어 user 상태 값이 바뀌고, state 값이 바뀌었기 때문에 다시 리렌더링이 일어나게 되므로 무한 루프에 빠지게 됨
import React, { useState, useEffect } from "react";
function Profile({ userId }) {
const [user, setUser] = useState(null);
const fetchUser = useCallback(
() =>
fetch(`https://your-api.com/users/${userId}`)
.then((response) => response.json())
.then(({ user }) => user),
[userId]
);
useEffect(() => {
fetchUser().then((user) => setUser(user));
}, [fetchUser]);
// ...
}
- useCallback을 사용하면 fetchUser 함수의 참조값을 동일하게 유지시킬 수 있음
- api 옵션으로 사용되는 userId가 변동될 때만 fetchUser에 새로운 함수가 할당되도록 설정하고, 그것이 아니면 동일한 함수가 실행되게 되어 무한 루프에 빠지지 않도록 할 수 있음
'React' 카테고리의 다른 글
Test Driven Development, React Testing Library (0) | 2023.08.01 |
---|---|
React Router Dom, APIs (0) | 2023.07.28 |
불변성 지키기 (0) | 2023.07.11 |
State, Props (0) | 2023.07.04 |
React Hooks (0) | 2023.07.03 |