React 처럼 생각하기(2)
리액트 문서 보며 정리
2021-12-12
렌더링 최적화
1. 컴포넌트를 맵핑할때는 key값으로 Index를 사용하지 말자
리액트에서 컴포넌트를 맵핑할 때에는 고유 Key값을 부여해야한다.
지금까지는 거의 Index를 고유한 Key값으로 사용했는데, 안좋은 습관인걸 알게됐다.
배열 중간에 어떤 요소가 삽입되게 된다면, 그 이후의 요소들은 전부 Index가 변경되기 때문에 Key값도 변경될 뿐만 아니라 리마운트가 실행된다.
또 이렇게 자주 Key값이 변경되게 된다면 서로 꼬이게 되는 버그도 발생할 수 있다.
하지만 무조건 사용하면 안되는 것은 아니다.
- 추가, 수정, 삭제 없이 단순히 렌더링만 하는 경우
- unique한 값이 없을 경우
- 정렬이나 필터 요소가 없는 경우
2. state는 어디에 선언해야 될까
리액트는 특정 state나 Props가 변경되게 되면 선언된 컴포넌트에서 그 하위 컴포넌트들 까지 모두다 리렌더링이 된다.
그렇기 때문에 state를 어디에 선언할지 잘 설계만 하더라도 불필요한 리렌더링을 막을 수 있다.
또 객체 타입의 state는 최대한 쪼개어서 선언해 사용하는게 좋다.
객체가 크고 복잡한 경우에 그 중 일부만 사용하더라도 관련된 모든 컴포넌트가 리렌더링 될것이다.
3. useState의 함수형 업데이트
setState를 사용할때 새로운 상태를 파라미터로 넣어주는 대신에, 상태 업데이트를 어떻게 할지 정의해 주는 업데이트 함수를 넣을 수 있다.
이렇게 하면 useCallback을 사용할 때 두 번째 파라미터로 넣는 배열에 값을 넣어주지 않아도 된다.
// 기존
const onIncrease = useCallback(() => {
setNumber(number + 1);
}, [number]);
// 함수형 업데이트
const onIncrease = useCallback(() => {
setNumber((number) => number + 1);
}, []);
4.Input Onchange 최적화
보통 Input 태그에 onChange를 사용해 글을 입력하면 타이핑을 할 때마다 컴포넌트가 랜더링이 된다.
이때 useMemo를 사용해주면 렌더링을 막을 수 있다.
import React, { useState } from "react";
function InputSample() {
const [text, setText] = useState("");
const [count, setCount] = useState("");
const onChange = (e) => {
setText(e.target.value);
};
const onCount = () => {
console.log("카운트..");
return count;
};
const outCount = onCount();
return (
<div>
<input onChange={onChange} value={text} />
<div>
<b>값: {text}</b>
<b>{outCount}</b>
</div>
</div>
);
}
export default InputSample;
{...}
const onChange = (e) => {
setText(e.target.value);
};
const onCount = () => {
console.log("카운트..");
return count;
};
const outCount = useMemo(() => onCount(), [count]);
return (
{...}
)
5. memo, useMemo, useCallback
- memo
React.memo는 Hook이 아니기 때문에 클래스형에서도 사용할 수 있다.
memo를 사용해 컴포넌트의 Props가 바뀌지 않았다면, 리렌더링하지 않도록 설정해 성능을 최적화 할 수 있다.
import React, { memo } from "react";
function InputSample({ user }) {
console.log("Text component render");
return (
<div>
<b>{user?.id}</b>
<b>{user?.name}</b>
<b>{user?.age}</b>
</div>
);
}
export default memo(InputSample);
- useMemo, useCallback
위에서 사용한 onCount의 함수가 간단한게 아니라 크고 복잡해 계산하는데 오래 걸리는 함수라고 가정한다면, 위 컴포넌트는 리렌더링 될 때마다 큰 계산을 계속 할 것이다.
useMemo [] 디펜던시에 데이터가 변할 때마다 함수를 실행할 수 있도록 count를 넣어주면 위 count가 변할 때마다 위 함수를 실행하게 된다.
useCallback도 비슷한 방식으로 작동 된다.
useMemo는 리턴되는 값을 memoize 시키는 반면에, useCallback은 함수 선언을 memoize 한다.
const addUser = useCallback(() => {
{...}
}, [users]);