css, scss 등의 별도 파일로 적용
컴포넌트 태그 안에 style
속성을 지정, props
를 이용한 동적 적용이 가능.
외부 라이브러리를 사용하는 방식 emotion
등이 있다.
useMemo
컴포넌트 리렌더링 발생 상황은 다음과 같다.
1. 자신의 상태가 변경될 때 (함수 컴포넌트의 경우만)
2. 부모 컴포넌트로 부터 받는 props가 변경될 때
3. 부모 컴포넌트의 상태가 변경될 때
컴포넌트가 리렌더링되면 말 그대로 JSX를 반환하는 함수를 다시 실행하는 것이고 이 과정에서 불필요한 동작이 있을 수 있다. 이렇게 불필요한 코드 실행을 방지하기 위해서 사용할 수 있는 Hook이 useMemo
이다.
useEffect
와 비슷하게 감시할 인자와, 그 인자가 변경될 때 실행할 함수를 매개변수로 대입한다.
코드
function App() { const [data, setData] = useState(0) return ( <div className="App"> <button onClick={() => setData(data + 1)}> Change Data </button> <Component data={data}> </div> ) }
function calculate(data) { let result = data //매우매우 복잡하고 오래걸리는 연산 return result } function Component({ data }) { // data의 값이 변경되었을 때만 'calculate' 함수가 실행된다. const result = useMemo(calculate(data), [data]) return ( <div> {result} </div> ) }
위 처럼 Component의
result
에 영향을 주는data
값에 변경이 있을 경우만calculation
함수가 실행되게 할 수 있으며, 이를 통해서 불필요한 동작을 막을 수 있다.
React.memo
위에서 설명했듯 부모 컴포넌트의 상태가 변경될 때 자식 컴포넌트의 리렌더링이 발생한다. useMemo
를 사용해서 일부 최적화가 가능하지만 리렌더링 자체를 막을 수는 없었다. 이 경우 React.memo
를 사용하여 부모 컴포넌트의 상태 변경시 리렌더링을 막을 수 있다.
JSX를 반환하는 함수를 React.memo()
로 감싸서 사용한다.
코드
import React from 'react' const Memo = React.memo(({ memo }) => { console.log('memo rendered') return <div>Memo: {memo}</div> }) export default Memo
import React from 'react' function Memo({ memo }) { console.log('memo rendered') return <div>Memo: {memo}</div> } export default React.memo(Memo)
위 처럼
React.memo
를 사용해서 props의 변화가 있을 경우만 렌더링이 진행되도록 할 수 있다. 하지만 props의 변화 감지가 얕은 비교를 통해서 이루어지므로 참조형 데이터가 props인 경우 이를 비교하여 true, false 값을 반환하는 비교 함수를 두 번째 인자에 추가할 수 있다.
관련 문서
useCallback
useCallback
의 사용 목적을 알기 위해서 리렌더링 동작에서 함수의 재정의 개념을 이해해야 한다. 이에 대해서 내가 이해한대로 정리하면 아래와 같다.
위의 예시 코드에서 Childe
컴포넌트는 렌더링시에 메세지를 출력한다
(f12
를 누르면 나오는 개발도구의 콘솔창에 출력된다).
Child
컴포넌트는 React.memo
를 사용해서 부모 컴포넌트의 상태가 변경되어도 리렌더링이 발생하지 않아야 하지만 Change State
버튼을 클릭해 부모 컴포넌트인 App
의 상태를 변경하면 메세지가 출력된다. 이는 다음과 같은 일련의 과정 때문이다.
App
의 상태가 변경되어 리렌더링App
의 리렌더링 과정에서 onClick
함수가 재정의onClick
함수는 Child
의 props이므로 Child
가 리렌더링즉, App
이 리렌더링 될 때 onClick
함수는 변경이 없어보이지만, 이름과 내용이 동일한 또 다른 함수로 대체되는 것이다.
위의 예시에서 알 수 있듯이 자식 컴포넌트에 props로 callback 함수를 사용하면 실행 내용에 변경이 없어도 불필요한 렌더링이 발생할 수 있다. 이를 방지하기 위해서 useCallback
을 사용한다.
렌더링 시에 재정의를 막을 함수와 의존성 배열을 매개변수로 입력한다.
function App() {
const [state, setState] = useState(0)
const onClick = useCallback(() => console.log('click'), [])
return (
<div className="App">
<h2> App </h2>
<p>state: {state}</p>
<button onClick={() => setState(state + 1)}>Change State</button>
<Child onClick={onClick}/>
</div>
);
};
공식문서
UI를 분리하여 관리할 수 있는 라이브러리에 대해서 간단한 예제를 직접 실행해봤다. 프로젝트 진행에 있어 고려할만한 라이브러리였다.
정리를 하면서 내가 이해한 것을 설명하는 것이 가장 어렵다는 것을 새삼 느낀다. 다른 사람이 봤을 때 이해하기 쉽게 정리하고 싶었는데, 뭔가 내용만 길어진 느낌이다.