사실 React에서 성능 최적화는 중요하지 않습니다.
코드 1000줄 정도 리팩토링하면 0.001초 정도 개선될 수 있을까요..?
하지만 중요하지 않은 것과 내가 성능에 대해 알지 못하는 것은 다르죠!
내용을 충분히 익히고 성능 최적화가 필요한 그 순간!
오늘의 지식이 빛을 발할거라고 생각합니다 ✨
fetchData
가 무슨 데이터를 가져오는건지 알 수 없다. useEffect(() => {
fetchData().then((res) => userListStorage.save(res))
}, [])
handleClick
함수 만들어서, 그 함수에서 두 기능의 함수 호출하자! <InputList>
<Button onClick={() => { resetNumber(); openModal(); // ❌ }}>카드 입력</Button>
</InputList>
git이 파일명의 대소문자 변경을 구분하지 못한다면 다음과 같은 설정해줘야 한다
git config core.ignorecase false
➡ Props 값이 변하지 않으면 다시 렌더하지 않음 (feat. memoizing, memoization)
➡ 부모가 rerender 하면, 자식 컴포넌트도 모두 rerender 된다!
Modal
컴포넌트의 title
값이 바뀌면? const Modal = ({ title }) => {
const [btnColor, setBtnColor] = useState('yellow');
return (
<div>
<p>{title}</>
<button onClick={() => { setBtnColor('black') }}>Change Color!</button>
<CounterButton color={btnColor} />
</div>
);
}
Modal
컴포넌트는 리렌더링 된다CounterButton
는 btnColor
props의 변경사항이 없는데도 리렌더링 된다.➡ class 컴포넌트는 React.PureComponent
,
functional 컴포넌트는 React.memo
를 사용해서
props 값이 변하지 않으면 다시 렌더링 하지 않도록 CounterButton
를 고친다!
(멘토님의 조언)
handleClick() {
this.setState(state => ({
words: state.words.concat(['marklar']) // ❌
}));
}
function updateColorMap(colormap) {
colormap.right = 'blue'; // ❌
}
- (변경 후)
- ES6의
spread syntax
handleClick() { this.setState(state => ({ words: [...state.words, 'marklar'], // ✅ })); };
- ES6의
Object.assign
function updateColorMap(colormap) { return Object.assign({}, colormap, {right: 'blue'}); // ✅ }
- ES8의
object spread properties
function updateColorMap(colormap) { return {...colormap, right: 'blue'}; // ✅ }
() => { alert('clicked!!'); }는 함수
다함수는 참조형!
따라서 다시 렌더링 될때마다 새로 생성된다.useCallback
으로 해결해보자!function Modal() {
return (
<CounderButton handleClick={() => { alert('clicked!!'); }} /> // ❌
);
}
- (변경 후)
useCallback
으로onHandleClick
함수를 새로 만들어서 props로 준다!function Modal() { const onHandleClick = useCallback(() => { // ✅ alert('clicked!!'); }); return ( <CounderButton handleClick={onHandleClick} /> // ✅ ); }
{ name: 'About', link: '/about' } 객체도 참조형
이다function TopBanner() {
return (
<MenuList menu={{ name: 'About', link: '/about' }} />
);
}
- (변경 후)
MENU_ITEM
으로 상수로 따로 빼두면 매번 다시 리렌더링 되지 않는다.function TopBanner() { return ( <MenuList menu={MENU_ITEM} /> // ✅ ); } const MENU_ITEM = { // ✅ name: 'About', link: '/about' }
BigButton
이 다시 렌더링 될 때마다, changeColor
함수를 생성한다는 문제점이 있다.async 함수
를 useEffect
에 넣어서 사용하면 되지만, useCallback
을 사용한다면..?function BigButton({ message }) {
const [color, setColor] = useState();
async function changeColor(isPrimary) {
const data = await fetchColor(message.type, isPrimary);
setColor(data.color);
}
useEffect(() => {
changeColor(true);
}, [changeColor])
}
(변경 후)
changeColor
함수가 정말로 필요할 때만 호출되도록 해야한다function BigButton({ message }) { const [color, setColor] = useState(); const changeColor = useCallback(// ✅ `useCallback` 사용해서 `message 가 변경되었을 때만` changeColor가 갱신되도록 함 async isPrimary => { const data = await fetchColor(message.type, isPrimary); setColor(data.color); }, [message] ); useEffect(() => { changeColor(true); }, [changeColor]) }
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
const id = setInterval(() => {
setCount(count + 1);
}, 1000);
return () => clearInterval(id);
}, [count]);
return <h1>{count}</h1>;
}
(변경 후) 의존성 배열을 최대한 줄이자!
function Counter() { const [count, setCount] = useState(0); useEffect(() => { const id = setInterval(() => { setCount(c => c + 1); // ✅ This doesn't depend on `count` variable outside }, 1000); return () => clearInterval(id); }, []); // ✅ return <h1>{count}</h1>; }
💡 컴포넌트를 매개변수으로 받아서 새로운 컴포넌트를 반환하는 함수다.
➡ .vscode/launch.json
을 만들고 F5로 실행
// launch.json
{
"version": "0.2.0",
"configurations": [
{
"name": "Chrome",
"type": "chrome",
"request": "launch",
"url": "http://localhost:3000",
"webRoot": "${workspaceRoot}/src"
}
]
}