티스토리 뷰
최적화 종류
리렌더링 조건
1. 컴포넌트의 state가 바뀌었을 때
2. 컴포넌트가 내려받은 props 가 변경되었을 때
3. 부모 컴포넌트가 리렌더링되면 자식도 모두 리렌더링 된다
컴포넌트란?
- 어떤 기능을 모아서 독립적으로 사용하는 함수로, 기능의 집합 같은 것이다
- 컴포넌트 내부에는 컴포넌트에 관한 것들로만 채워야 한다(독립적인 사용과 협업 시 혼선을 주지 않기 위해)
- 컴포넌트 내부 함수들은 컴포넌트의 기능을 위한 메서드와 같은 것이다.
- 반복적인 함수는 컴포넌트를 분리하여 관리하고, 하나의 컴포넌트만 사용하는 함수는 내부에 선언하여 사용된다.
- 스타일의 경우, 전역으로 선언하여 다른 컴포넌트들이 접근할 수 있게 하거나, 스타일을 위한 컴포넌트를 생성하여 관리하는 것이 제일 효율적이다.
React.memo
- 캐싱 - 메모리에 임시 저장
- 컴포넌트를 캐싱한다,
- 컴포넌트를 메모리에 저장해 두고 필요할 때 갖다 쓰는 것 그러면 부모 컴포넌트의 state 변경으로 props가 변경되지 않는 한 자식 컴포넌트는 리렌더링 하지 않는다 (컴포넌트 임시 저장소)
사용 방법
- App 컴포넌트의 이벤트를 작동시키면 BOX 컴포넌트도 같이 리렌더링 된다
- 리렌더링 하지 않아도 되는 컴포넌트 export를 React.memo로 감싸주면 App 컴포넌트 이벤트에 영향받지 않게 된다
function App() {
console.log("App 컴포넌트가 렌더링되었습니다!");
const [count, setCount] = useState(0);
// 1증가
const onPlusBtn = function () {
return setCount(count + 1);
};
// 1감소
const onMinusBtn = () => {
setCount(count - 1);
};
return (
<>
<h3>카운드 예제입니다</h3>
<p>현재 카운트 : {count}</p>
<button onClick={onPlusBtn}>+</button>
<button onClick={onMinusBtn}>-</button>
<div style={{ display: "flex", marginTop: "10px" }}>
<Box1 />
<Box2 />
<Box3 />
</div>
</>
);
}
function Box1({ initCount }) {
console.log("Box1 컴포넌트가 렌더링 되었습니다");
return (
<div style={style}>
<button
onClick={() => {
initCount();
}}
>
초기화
</button>
</div>
);
}
// 부모 컴포넌트의 state가 변경돼 props가 변하지 않는 한 자식 컴포넌트는 리렌더링 되지 않는다.
// memo - 컴포넌트 캐싱(컴포넌트를 메모리에 저장하여 필요할때 사용한다)
export default React.memo(Box1);
useCallback
- 캐싱 - 메모리에 임시 저장
- 함수를 캐싱한다, 함수를 useCallback으로 감싸주게 되면 App 컴포넌트가 렌더링 될 때 함수 그대로 메모리에 저장하여 의존성배열[ ] 부분의 값이 변하지 않는 한 초기화되지 않는다(빈 값도 마찬가지)
- 메모리에 initCount 함수를 저장하여 최적화하는 것 (임시 저장소)
useCallback , useEffect 차이
- useCallback은 컴포넌트 최적화를 위해 함수를 메모리에 저장하는데 목적이 있기 때문에 의존성 배열의 값이 변경되지 않으면 해당 함수는 app컴포넌트가 리렌더링 돼도 초기화되지 않는다, (최초 렌더링 시 그대로) 함수 임시 저장소 느낌
- useEffect는 컴포넌트 렌더링 시 내부 로직 실행에 목적이 있다. 렌더링 시 무조건 실행 또는 의존성 배열의 값이 변경될 때마다 실행
사용 방법
- initCount를 useCallback 없이 실행시키면 React.memo hook을 사용했음에도, app컴포넌트의 state가 변경되면서 리렌더링 될 때마다 함수를 초기화시킨다
- 때문에 box1으로 전달하는 props가 바뀌었다고 판단하여 box1도 같이 리렌더링 된다 이것을 막기 위해 useCallback을 사용한다.
- useCallback으로 initCount를 감싸면 App 컴포넌트가 렌더링 될 때 initCount함수는 메모리에 그대로 저장되어 [ ]에 값이 변하지 않는 한 초기화되지 않는다(빈 값도 마찬가지)
- useCallback으로 app컴포넌트의 렌더링 시점(count=0)에 저장되었기 때문에 값은 변하지 않는다, 하지만 count를 [ ]에 넣어줄 경우, 값이 변할 때마다 리렌더링 된다
// count를 초기화 하는 함수
const initCount = useCallback(() => {
console.log(`${count} 에서 0 으로 변경되었습니다`);
setCount(0);
}, [count]);
useMemo
- 캐싱- 메모리에 임시 저장
- useMemo : 값을 캐싱한다, 함수가 반환하는 값 자체를 메모리에 저장한다
- 저장 값은 변수, 배열, 객체 등 데이터를 저장하여 사용하도록 한다
- 무거운 작업(엄청 많은 양의 데이터를 반복해야 할 경우) 사용
사용 방법_1
- HeavyComponent에 많은 데이터를 반복하는 함수가 있다
- 컴포넌트가 렌더링 될 때마다, 함수가 초기화되어 다시 실행되고 많은 양의 데이터를 한꺼번에 처리하려 하다 보니, 렌더링 시간이 지연된다
- useMemo로 데이터(값)를 감싸주면 컴포넌트의 최초 렌더링 시 해당 값이 메모리에 저장되므로 [의존성 배열] 이 변하지 않으면 함수가 초기화되지 않아 속도가 안정적 이게 된다
// heavy work > 엄청 무거운 작업
function App() {
return (
<>
<nav style={{ backgroundColor: "yellow", margin: "30px" }}>
네비게이션 바
</nav>
<HeavyComponent />
<footer style={{ backgroundColor: "green", margin: "30px" }}>
푸터 영역입니다.
</footer>
</>
);
}
function HeavyComponent() {
const [count, setCount] = useState(0);
const heavyWork = function () {
for (let i = 0; i < 2000000000; i++) {}
return 100;
};
// const value = heavyWork(); // useMemo 사용 전
const value = useMemo(() => heavyWork(), []);
console.log("value는 ", value);
return (
<>
<p>나는 엄청 무거운 컴포넌트야</p>
<button onClick={() => setCount(count + 1)}>
누르면 아래 count가 올라가요!
</button>
<br />
{count}
</>
);
}
사용 방법_2
- useEffect는 컴포넌트가 렌더링 되거나 의존성 배열에 담긴 값이 변경될 때 실행된다, 근데 setUselessCount state가 변경될 때도 작동된다. 원래 둘은 아무 상관이 없기 때문에 작동하지 않아야 한다.
- 원인은, setUselessCount state가 변경될 때마다 컴포넌트가 리렌더링하기때문에 렌더링 시 실행되는 useEffect도 영향을 받는 것. 불변성의 법칙과 같다
- 컴포넌트 내부 함수들은 객체와 같음, 주소를 참조한다 리렌더링 될때마다 새로운 메모리에 할당하기 때문에 주소값이 달라진다
- me 객체를 useMemo에 담아 렌더링 시 데이터에 저장하고 생사 토글이 작동할 때만 useMemo가 갱신되도록 하면 아래 토글의 영향을 받지 않게 된다.
function ObjectComponent() {
const [isAlive, setIsAlive] = useState(true);
const [uselessCount, setUselessCount] = useState(0);
const me = useMemo(() => {
return {
name: "Ted Chang",
age: 21,
isAlive: isAlive ? "생존" : "사망",
};
}, [isAlive]);
useEffect(() => {
console.log("생존여부가 바뀔 때만 호출해주세요!");
}, [me]);
return (
<>
<div>
내 이름은 {me.name}이구, 나이는 {me.age}야!
</div>
<br />
<div>
<button
onClick={() => {
setIsAlive(!isAlive);
}}
>
누르면 살았다가 죽었다가 해요
</button>
<br />
생존여부 : {me.isAlive}
</div>
<hr />
필요없는 숫자 영역이에요!
<br />
{uselessCount}
<br />
<button
onClick={() => {
setUselessCount(uselessCount + 1);
}}
>
누르면 숫자가 올라가요
</button>
</>
);
}
'TIL > React' 카테고리의 다른 글
[리액트 React] 팬카페 만들기_메뉴마다 다른 색상 넣기 (0) | 2024.01.31 |
---|---|
[리액트 React] 실제 돔과 가상 돔 (DOM, Virtual DOM) (0) | 2024.01.30 |
[리액트 React] 리액트 훅(React Hook) (0) | 2024.01.25 |
[리액트 React] 스타일 컴포넌트(Styled Components) 지역,전역 스타일 (0) | 2024.01.24 |
[리액트 React] 투두리스트 TodoList, Jsx문법, 컴포넌트 (0) | 2024.01.23 |