
Memoization
- 기존에 수행한 연산의 결과값을 저장해두고 동일한 입력이 들어오면 재활용하는 기법이다.
- Memoized된 내용을 재사용하여 렌더할 시, 가상 DOM에서 바뀐 부분을 확인하지 않아 성능이 향상된다.
Memoization 방법
1️⃣ Redux - useSelector
- useSelector로 Redux를 최적화하는 방법
① useSelector를 여러번 사용
- store에서 객체를 통째로 불러오는 대신, 필요한 값들을 쪼개어서 useSelector로 선언해준다.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const number = useSelector(state => state.counter.number); | |
const diff = useSelector(state => state.counter.diff); |
└ number 또는 diff가 바뀌었을 때만 리렌더링 된다.
② shallowEqual 사용
- shallowEqual은 react-redux에 내장되어있는 함수로, 객체 안의 가장 겉에 있는 값들을 비교해준다.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const { number, diff } = useSelector( | |
state => ({ | |
number: state.counter.number, | |
diff: state.counter.diff | |
}), | |
shallowEqual | |
); |
③ equality function 사용
- useSelector 두번째 인자에 equality function을 직접 작성하여 추가한다.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const result = useSelector(functionA, equalityFn) |
2️⃣ React.memo
- 컴포넌트를 React.memo()로 래핑시, 렌더링 결과를 Memoizing하고 다음 렌더링이 일어날 때 props가 일치한다면, React는 Memoizing된 내용을 재사용한다.
언제 사용할까 ?
⑴ Pure Functional Component에서 (클래스형 컴포넌트 사용 X)
⑵ Rendering이 자주 일어날 경우
⑶ 동일한 props => 동일한 렌더링 결과를 제공할 경우
⑷ UI element의 양이 많은 컴포넌트의 경우
예시
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react'; | |
const CreateUser = ({ **username, email, onChange, onCreate }) => { | |
return ( | |
<div> | |
<input | |
name="username" | |
placeholder="계정명" | |
onChange={onChange} | |
value={username} | |
/> | |
<input | |
name="email" | |
placeholder="이메일" | |
onChange={onChange} | |
value={email} | |
/> | |
<button onClick={onCreate}>등록</button> | |
</div> | |
); | |
}; | |
export default React.memo(CreateUser); |
3️⃣ useMemo
- 사용방법을 제외하고는 React.memo와 매우 흡사하다.
- React.memo가 component의 결과 값을 memoizing하여 불필요한 re-rendering을 관리한다면,
- useMemo는 함수의 결과 값을 memoizing하여 불필요한 연산을 관리한다.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const computeExpensiveValue(a, b) = 비싼 함수 계산; | |
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]); |
- useMemo의 [dependency]가 변경되었을 때에만 메모이제이션된 값을 다시 계산한다.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SLOW | |
const sortedWords = ~~sortWords~~(); | |
//FAST | |
const sortedWords = useMemo(sortWords, [words]); |
4️⃣ useCallback
- useMemo는 특정 결과값을 재사용 할 때 사용하는 반면, useCallback은 특정 함수를 새로 만들지 않고 재사용하고 싶을 때 사용한다.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const App =()=>{ | |
//함수 선언 | |
const onToggle = **useCallback**(id => { | |
setUsers( | |
users.map(user => | |
user.id === id ? { ...user, active: !user.active } : user | |
) | |
); | |
}, [users]); | |
.... | |
return ( | |
//prop으로 함수를 pass down | |
<CreateUser onToggle={onToggle} /> | |
) | |
} |
주의점 : 메모이제이션용 메모리가 추가로 필요하므로, 불필요한 props 비교 절약을 위해서
useCallback, useMemo, React.Memo는 컴포넌트의 성능을 실제로 개선할 수 있는 상황에서만 사용해야 한다.
5️⃣ Portals
- 부모 컴포넌트 바깥에 있는 DOM 노드로 자식을 렌더링할 수 있게 해주는 기능이다.
(UI를 어디에 렌더링 시킬지 DOM을 사전에 선택하여 부모 컴포넌트의 바깥에 렌더링 할 수 있게 해주는 기능)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
ReactDOM.createPortal(child-렌더링 대상, container-DOM 엘리먼트) |
- 존재 위치는 부모 바깥이지만, 모든 다른 면에서 일반적인 React 자식처럼 동작한다. (ex- context, props, 이벤트 버블링)
└ DOM 트리 위치는 바뀌지만, portal은 여전히 React 트리에 존재하기 때문이다. - 모달, 호버카드, 툴팁과 같은 경우에 주로 사용된다.
└ 부모 컴포넌트에 overflow: hidden이나 z-index가 있는 경우 주로 사용한다.
부모 컴포넌트
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import PortalExample from "./Portal"; | |
import React, { useRef } from "react"; | |
function App() { | |
const modalRef = useRef(); | |
const openModal = () => { | |
// 자식에서 정의한 함수를 trigger | |
modalRef.current.openModal(); | |
}; | |
return ( | |
<div className="App"> | |
<PortalExample ref={modalRef}> | |
<h1>Modal Header</h1> | |
<p> lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum </p> | |
</PortalExample> | |
<button onClick={openModal}>Open Portal Modal</button> | |
</div> | |
); | |
} | |
export default App; |
자식 컴포넌트
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { useState, forwardRef, useImperativeHandle } from "react"; | |
import ReactDOM from "react-dom"; // | |
//ref 부모로부터 전달받기 위해 두번째 인자로 ref를 넣어줌. | |
const PortalExample = forwardRef((props, ref) => { | |
const [display, setDisplay] = useState(true); | |
//useImperativeHandle을 통해 App.js(부모) 에서 아래 함수 사용가능 | |
useImperativeHandle(ref, () => { | |
return { | |
openModal: () => open(), | |
close: () => close(), | |
}; | |
}); | |
//전달할 함수 | |
const open = () => { | |
setDisplay(true); | |
}; | |
const close = () => { | |
setDisplay(false); | |
}; | |
return ( | |
display && | |
ReactDOM.createPortal( | |
<Portal> | |
<div className="modal-backdrop" onClick={close}></div> | |
<div className="modal-box">{props.children}</div> | |
</Portal>, | |
document.getElementById("modal-root") | |
) | |
); | |
}); | |
const Portal = styled.div` | |
position: fixed; | |
.modal-backdrop { | |
position: fixed; | |
top: 0; | |
} | |
.modal-box { | |
position: relative; | |
top: 50%; | |
} | |
`; | |
export default PortalExample; |
'React > 2022-上' 카테고리의 다른 글
원시 자료형 vs 참조 자료형 (0) | 2022.05.01 |
---|---|
Reduce (0) | 2022.05.01 |
Optimistic UI (0) | 2022.05.01 |
LazyLoad vs Preload (0) | 2022.05.01 |
Promise all (0) | 2022.05.01 |