순서대로 코드를 수행하면서 어떤 컴포넌트들이 업데이트되는지?
1) setCount(10)이 실행
2) App 컴포넌트의 count state 변화
3) App 컴포넌트 state 업데이트되면서 리렌더링
4) 자식 컴포넌트인 CountView 또한 리렌더링
연산 낭비
: TextView는 text state가 바뀐게 아니므로 리렌더링될 필요가 없음 (App 컴포넌트가 업데이트되었다고 해도 TextView 컴포넌트가 받고 있는 Prop인 text가 바뀐 상황이 아니므로)각각 업데이트 조건을 걸어서
우리의 성능을 지키자!고차 컴포넌트(HOC)
: 컴포넌트를 가져와서 새 컴포넌트를 반환하는 함수가죽을 주면 구두를 만들어 주겠다
: 함수를 호출할 때 매개변수로 컴포넌트를 전달했더니 더 강화된 컴포넌트로 돌려주겠다React.memo
는 고차 컴포넌트똑같은 Props를 받으면 똑같은 걸 내놓겠다
는 뜻 = 컴포넌트한테 똑같은 Props가 바뀐 것처럼 줘도 이거 똑같잖아하고 다시 컴포넌트를 계산안할래
= 리렌더링하지 않겠다
는 뜻React.memo
는 우리가 리렌더링하지 않았으면 하는 컴포넌트에 감싸주면 Props가 바뀌지 않으면 리렌더링하지 않은 강화된 컴포넌트로 돌려주겠다는 것 (다만, state가 바뀌면 리렌더링은 됨)src/App.js
import OptimizeTest from "./OptimizeTest";
function App() {
return (
<div className="App">
<OptimizeTest />
</div>
);
}
src/OptimizeTest.js
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;
얉은 비교
: 객체의 주소
에 의한 비교를 하기 때문에areEqual 함수
: props들이 서로 같으면 true 반환, props들이 서로 다르면 false 반환 (areEqual 함수에 의해 판단)src/OptimizeTest.js
import React, { useState, useEffect } from "react";
const CounterA = React.memo(({count}) => {
useEffect(()=>{
console.log(`CounterA Update - count: ${count}`);
});
return <div>{count}</div>
});
// CounterB도 처음엔 똑같이 React.memo로 감싸주었다가
// prop인 obj가 객체이기 때문에 (얇은 비교로 인해) 해제시킴
const CounterB = ({obj}) => {
useEffect(()=>{
console.log(`CounterB Update - count: ${obj.count}`);
});
return <div>{obj.count}</div>
};
// areEqual 함수 사용하여 props들 비교
const areEqual = (prevProps, nextProps) => {
// return true // 이전 Props와 현재 Props가 같다 > 리렌더링을 일으키지 않음
// return false // 이전과 현재가 다르다 > 리렌더링을 일으킴
if(prevProps.obj.count === nextProps.obj.count){ return true; }
return false;
};
// 매개변수로 CounterB 함수와 areEqual 함수 전달
// CounterB는 areEqual 함수의 판단에 따라 리렌더링을 할지 말지 결정
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;