일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 타입 좁히기
- 이분 검색
- Jest
- TS
- autosize
- recoil
- React
- app router
- 반공변성
- 호이스팅
- dfs
- async/await
- 인터섹션
- 리터럴 타입
- SSR
- map
- 결정 알고리즘
- Cypress
- ESlint
- RTK Query
- tailwind
- webpack
- CI/CD
- Promise
- CORS
- 무한 스크롤
- 태그된 유니온
- 투포인터
- useAppDispatch
- 공변성
- Today
- Total
짧은코딩
최적화2-컴포넌트 재사용 본문
App에서 state로 count, text가 있다.
그리고 자식 컴포넌트에 CountView는 count를 받고 TextView는 text를 받는다고 한다.
그럼 count가 변경되었을 때, 모두 자식도 모두 리렌더 된다. 하지만 TextView는 리렌더 될 필요가 없다.
=> 자식 컴포넌트에게 업데이트 조건으로 각각 count와 text가 변경될 때만 업데이트 되도록 조건을 걸면된다. 이것을 할 수 있는 것이 React.memo이다.
-리엑트 공식 문서
이 사이트를 잘 활용하여 리엑트를 배우면 좋다.
React.memo
https://ko.reactjs.org/docs/react-api.html#reactmemo
위 사이트에서 React.memo 설명이다.
-OptimizeTest.js
import React, { useState, useEffect } from "react";
const Textview = ({ text }) => {
useEffect(() => {
console.log(`Update :: Text : ${text}`);
});
return <div>{text}</div>;
};
const CountView = ({ count }) => {
useEffect(() => {
console.log(`Update :: count : ${count}`);
});
return <div>{count}</div>;
};
const OptimizeTest = () => {
const [count, setCount] = useState(1);
const [text, setText] = useState("");
return (
<div style={{ padding: 50 }}>
<div>
<h2>count</h2>
<CountView count={count} />
<button onClick={() => setCount(count + 1)}>+</button>
</div>
<div>
<h2>text</h2>
<Textview text={text} />
<input value={text} onChange={(e) => setText(e.target.value)} />
</div>
</div>
);
};
export default OptimizeTest;
Textview, Countview는 Optimizetest의 자식 컴포넌트이다.
-출력 결과
하나의 상태를 바꿀 때, 둘 다 출력이되어 낭비가 된다.
-React.memo 사용
import React, { useState, useEffect } from "react";
const Textview = React.memo(({ text }) => {
useEffect(() => {
console.log(`Update :: Text : ${text}`);
});
return <div>{text}</div>;
});
const CountView = React.memo(({ count }) => {
useEffect(() => {
console.log(`Update :: count : ${count}`);
});
return <div>{count}</div>;
});
const OptimizeTest = () => {
const [count, setCount] = useState(1);
const [text, setText] = useState("");
return (
<div style={{ padding: 50 }}>
<div>
<h2>count</h2>
<CountView count={count} />
<button onClick={() => setCount(count + 1)}>+</button>
</div>
<div>
<h2>text</h2>
<Textview text={text} />
<input value={text} onChange={(e) => setText(e.target.value)} />
</div>
</div>
);
};
export default OptimizeTest;
이렇게 자식 컴포넌트를 React.memo로 감싸면
이렇게 값이 바뀌면 둘 다 실행되지 않고 하나만 실행된다.
-다른 예시
import React, { useState, useEffect } from "react";
const CounterA = React.memo(({ count }) => {
useEffect(() => {
console.log(`CounterA Update - count: ${count}`);
});
return <div>{count}</div>;
});
const CounterB = React.memo(({ obj }) => {
useEffect(() => {
console.log(`CounterB Update - count: ${obj.count}`);
});
return <div>{obj.count}</div>;
});
const OptimizeTest = () => {
const [count, setCount] = useState(1);
const [obj, setObj] = useState({
count: 1,
});
return (
<div style={{ padding: 50 }}>
<div>
<h2>Counter A</h2>
<CounterA count={count} />
<button onClick={() => setCount(count)}>A button</button>
</div>
<div>
<h2>Counter B</h2>
<CounterB obj={obj} />
<button
onClick={() =>
setObj({
count: obj.count,
})
}
>
B Button
</button>
</div>
</div>
);
};
export default OptimizeTest;
Counter A는 자기 자신으로 바꿔서 달라지는게 없다.
Counter B도 obj.count로 바꿔서 값은 그대로이다.
결과는 A 버튼을 누르면 콘솔에 아무것도 안찍힌다.
그치만 B 버튼은 누르면 콘솔에 B가 업데이트 되었다고 출력된다.
=> 결국 리렌더링이 일어나서 출력이 되는 것이다. 이유는 obj가 객체라서이다. 리엑트에서는 객체를 비교할 때 얕은 비교를 한다.
-얕은 비교
객체 a와 b를 만들고 프로퍼티에 count: 1을 넣었다.
근데 그리고 a와 b를 비교하면 같지 않다는 결과가 나온다.
왜냐면 객체는 만들어지면 교유의 메모리 주소가 생긴다.
얕은 비교는 이 메모리 주소를 비교하기 때문에 같지 않다는 결과가 나온다.
하지만 a를 만들고 b에 a를 복사하고 비교하면 a와 b는 같다는 결과가 나온다.
-깊은 비교를 하도록 만드는 방법
const areEqual = (prevProps, nextProps)=>{
return true //이전 프롭스 현재 프롭스와 같다 -> 리렌더링을 일으키지 않음
return false //이전과 현재가 다르다 -> 리렌더링을 일으킨다
}
import React, { useState, useEffect } from "react";
const CounterA = React.memo(({ count }) => {
useEffect(() => {
console.log(`CounterA Update - count: ${count}`);
});
return <div>{count}</div>;
});
const CounterB = ({ obj }) => {
useEffect(() => {
console.log(`CounterB Update - count: ${obj.count}`);
});
return <div>{obj.count}</div>;
};
const areEqual = (prevProps, nextProps) => {
return prevProps.obj.count === nextProps.obj.count;
};
const MemoizedCounterB = React.memo(CounterB, areEqual);
const OptimizeTest = () => {
const [count, setCount] = useState(1);
const [obj, setObj] = useState({
count: 1,
});
return (
<div style={{ padding: 50 }}>
<div>
<h2>Counter A</h2>
<CounterA count={count} />
<button onClick={() => setCount(count)}>A button</button>
</div>
<div>
<h2>Counter B</h2>
<MemoizedCounterB obj={obj} />
<button
onClick={() =>
setObj({
count: obj.count,
})
}
>
B Button
</button>
</div>
</div>
);
};
export default OptimizeTest;
areEqual을 통해서 두 객체의 프로퍼티가 같은지 확인한다.
MemorizedCounterB는 2번째 인자에 따라서 리렌더링을 할지 안할지 결정한다.
그리고 MemorizedCounterB를 최종 리턴에 넣어주면 깊은 복사를 할 수 있다.
'인프런, 유데미 > 한입 크기로 잘라 먹는 리액트' 카테고리의 다른 글
최적화4-삭제, 수정 최적화 (0) | 2022.05.29 |
---|---|
최적화3-컴포넌트 최적화 (0) | 2022.05.29 |
최적화 1 - useMemo (0) | 2022.05.27 |
React developer tools (0) | 2022.05.27 |
React에서 API 호출하기 (0) | 2022.05.26 |