리액트는 컴포넌트를 렌더링 한 뒤에 이전 결과와 비교하여 DOM을 업데이트 한다.
컴포넌트를 나누는것은 재사용성을 위해서 이지만, 컴포넌트의 렌더링 최적화를 위해서 이기도 하다.
결국 렌더링이 최대한 덜 될 수 있있게 해야하는데, 이때 리 렌더링이 필요없는 컴포넌트의 렌더링을 방지하여 성능을 높일 수 있는 방법들이 있다.
컴포넌트 렌더링 최적화
리 렌더링을 방지하여 컴포넌트의 리렌더링 성능 최적화를 해줄 수 있다.
예를들어 todoList 리액트 앱에서
todo list form영역에서 input에 입력을 하게 된다면,
value를 가지고 있는 부분만 렌더링이 아닌 연관이 없는 컴포넌트까지 같이 렌더링이 되는것을 확인할 수 있다.
이러한 부분은 react.memo를 적용해서
컴포넌트에서 리렌더링이 필요한 상황에서만 리렌더링을 하도록 설정해 문제를 해결할 수 있다.
사용방법: 적용을 해야하는 컴포넌트를 memo로 감싸주면 된다.
import React from 'react'
import List from './List'
const Lists = React.memo(({ todoData, setTodoData }) => {
return (
<div>
{todoData.map((data) => (
<div key={data.id}>
<List
key={data.id}
id={data.id}
title={data.title}
completed={data.completed}
todoData={todoData}
setTodoData={setTodoData}
/>
</div>
))}
</div>
)
})
export default Lists
React.memo는 HOC이다.
HOC(Higher-Order Components) HOC란 컴포넌트를 인자로 받아서 새로운 컴포넌트를 return해주는 구조의 함수
HOC는 리액트의 API가 아닌, 리액트 컴포넌트를 구성하는데 일종의 패턴이다.
리액트는 참조 값만 비교하는 Shallow Copy(얕은 복사)를 실행한다.
즉 state가 변경될 때 shallow copy를 통해 같은 값인지 판단하고 렌더링 여부를 결정한다.
함수 최적화
컴포넌트가 렌더링 될 때 그 안에 있는 함수도 다시 그리게 된다.
컴포넌트가 리 렌더링 될때마다 똑같은 함수를 계속 만드는 것은 좋은 현상이 아니다.
만약 함수가 자식 컴포넌트에 props로 내려 줄 경우 함수를 포함하고 있는 컴포넌트가 리 렌더링 될 때마다 자식 컴포넌트의 props로 받은 함수도 새롭게 만들어지기 때문에 계속 리 렌더링을 하게 된다.
이러한 문제를 해결하기 위해 useCallback()을 사용한다.
const handleClick = useCallback((id=> {
let newTodoData = todoData.filter((data) => data.id !== id)
setTodoData(newTodoData)
localStorage.setItem("todoData", JSON.stringify(newTodoData))
},[todoData])
useCallback안에 콜백함수와 의존성배열을 순서대로 입력한다.
함수 내에서 참조하는 state, props가 있다면 의존성 배열에 추가한다.
todoData가 useCallback으로 인해 새로 생성되지 않기에 메모리에 새로 할당되지 않고 동일한 참조 값을 사용하게 된다.
의존성 배열에 아무것도 없다면 컴포넌트가 최초 렌더링 시에만 함수가 생성되고, 그 이후에는 동일한 참조값을 사용하는 함수가 된다.
결과 값 최적화하기
Memoization이란?
비용이 많이 드는 함수 호출의 결과를 저장하고 동일한 입력이 발생할때 캐시된 결과를 반환한다. 컴퓨터 속도를 높이는데 주로 사용되는 최적화 기술이다.
function Component({a, b}){
const result = compute(a, b)
return <div>{result}</div>
}
component내의 compute함수가 만일 복잡한 연산을 하게 된다면 결과 값을 리턴하는데 오랜시간이 걸리게 된다.
이런 상황에서 컴포넌트가 계속 리 렌더링이 될 경우라면, 성능에 좋지 않은 영향을 미치게 되며 UI지연 현상이 발생하게 된다.
이러한 현상을 해결해주기 위해 useMemo를 사용한다.
compute함수에 넘겨주는 값이 이전과 동일하다면, 컴포넌트가 리 렌더링이 되더라도 연산을 다시 하지 않고 저장해두었던 값을 재활용하게 된다.
function Component({a, b}){
const result = useMemo(() => compute(a, b), [a, b])
return <div>{result}</div>
}
useMemo로 감싸 준 후 첫번째 인수에는 의존성 배열 compute 함수에서 사용하는 값을 넣어준다.