✅ 알아두면 유용한 개발자 도구
설치는 구글 웹스토어에서 진행
1. Apollo Client Devtools
→ 설치후 app.tsx에서 client 설정 부분에
connectToDevTools : true로 설정해야 한다.2. wappalyzer
→특정 사이트에 들어가시면 해당 사이트가 사용한 스택을 분석해준다.3. react developer tools 설치
: 눈으로 직접 확인하며 개발할 수 있는 도구
-> 개발자 도구에 proflier에 생성됨
렌더링 될 대상일 때 영역을 표시해주는 것 체크하기!
import { useState } from "react"; export default function MemoizationPage(): JSX.Element { console.log("component 렌더링 됨"); let countLet = 0; const [countState, setCountState] = useState(0); const onClickCountLet = (): void => { console.log(countLet); countLet += 1; //countLet = countLet + 1; }; const onClickCountState = (): void => { console.log(countState); setCountState(countState + 1); }; return ( <> <div>COUNT(let) : {countLet} </div> <button onClick={onClickCountLet}>COUNT(let) + 1</button> <div>COUNT(state): {countState}</div> <button onClick={onClickCountState}>COUNT(state) + 1</button> </> ); }
let은 버튼을 누르면 콘솔의 값은 올라가지만 리렌더는 일어나지 않음!
따라서 "component 렌더링 됨" 이라는 콘솔이 찍히지 않고 있으며, 화면은 여전히 0 이다.하지만 state는 버튼을 누름과 동시에 리렌더링되고, 올려두었던 countLet이 0으로 초기화 된다.
이는 useState를 제외한 모든 값이 다시 그려지고 있다는 것!!
자식컴포넌트를 분리 후 연결하면?
툴에서 확인해보면 렌더링 많이 되니 노란색으로 바뀜
부모의 state를 바꿨는데 자식도 다시 렌더링된다.부모는 렌더링 되지만, 자식은 렌더링 하고 싶지 않을 땐?
✅ memo
import { memo } from "react"; function MemoizationWithChildPage(): JSX.Element { console.log("child 렌더링 됨"); return ( <> <div>======================</div> <h1>!!CHILD COMPONENT!!</h1> <div>======================</div> </> ); } export default memo(MemoizationWithChildPage); //memo = HOC
state카운트를 클릭하면 컨테이너만 렌더링이 된다.
memo를 걸어두면 처음의 값이 기억되어 업데이트 되지 않는다.
memo로 적용된 컴포넌트는 상위 컴포넌트가 재렌더가 되더라도,
넘어오는 props 의 값이 동일하다면 컴포넌트를 재렌더하지 않게 된다.부모컴포넌트에서도 부분적으로 렌더링이 일어나지 않아도 되는 부분이 있음!! => 규모가 커질수록 심해짐!
✅ useMemo() & useCallback()
불필요한 값들이 지속적으로 다시 만들어지지 않도록 유지시켜주는 hooks
useMemo( 연산 함수, 적용할 변수를 배열로 감싸기)
import { useCallback, useMemo, useState } from "react"; import MemoizationWithChildPage from "./02-child"; export default function MemoizationPage(): JSX.Element { console.log("component 렌더링 됨"); let countLet = 0; const [countState, setCountState] = useState(0); // 1. useMemo로 변수 기억 const aaa = useMemo(() => Math.random(), []); console.log(aaa);
useEffect처럼 의존성 배열을 사용할 수도 있다.
props로 넘겨줘야하는 것은 꼭 필요한 것만 넘겨주기!
useCallback()
// 2. useCallback으로 함수 기억 // useCallback을 사용하게 되면 함수를 다시 만들지 않는다. const onClickCountLet = useCallback((): void => { console.log(countLet); countLet += 1; //countLet = countLet + 1; }, []); // 3. useCallback으로 함수 기억 => state사용주의 const onClickCountState = useCallback((): void => { // console.log(countState); // setCountState(countState + 1); setCountState((prev) => prev + 1); //그때 그때 prev(기존값)를 가지고 와야함 }, []); // 4. 잘못된 사용사례_useMemo로 나만의 useCallback 만들어보기 // const onClickCountState2 = useMemo( // () => (): void => { // console.log(countState + 1); // setCountState(countState + 1); // }, // [] // ); //expected : 1111111111 //state를 기억하기 때문에 아무리 count를 올려도 1만 찍히게 된다.(이전에 불러왔던 값을 유지) => prev를 이용해서 함수를 다시 불러오지 않고 값만 올려주기! return ( <> <div>======================</div> <h1>!!PARENT COMPONENT!!</h1> <div>COUNT(let) : {countLet} </div> <button onClick={onClickCountLet}>COUNT(let) + 1</button> <div>COUNT(state): {countState}</div> <button onClick={onClickCountState}>COUNT(state) + 1</button> {/* <button onClick={onClickCountState2}>COUNT(state) + 1</button> */} <div>COUNT(state): {countState}</div> {/* 5. 잘못된 사용사례_로직과 UI가 합쳐져서 헷갈림 => 유지보수 힘듬, 메모이제이션 더 복잡!! */} {/* <button onClick={useCallback((): void => { setCountState((prev) => prev + 1); }, [])} COUNT(state) + 1 </button> */} <div>======================</div> <MemoizationWithChildPage /> </> ); }
다시실행되면 안되는게 메모
다시실행해주는게 콜백
❗️ useCallback을 쓰지 말아야 할 때
의존성 배열의 인자가 1~2개보다 많아질 때는 차라리 리렌더를 하는것이 유지 보수에는 더 좋은방법이다.
성능이 조금이나마 좋아지는 것 보다는 유지보수가 편리한 편이 훨씬 좋다.
따라서 의존성 배열의 인자가 2개를 초과할때는 그냥 리렌더를 해주시는게 좋음!!
✅ map과 memo의 관계
- parent.tsx
import { v4 as uuidv4 } from "uuid"; import { useState } from "react"; import Word from "./02-child"; export default function MemoizationWithMapParentPage(): JSX.Element { const [data, setData] = useState("HI THERE"); const onClickChange = (): void => { setData("BYE THERE"); }; return ( <> {/* 2. memo해도 , key자체가 매번 변경되어 props로 넘어가므로, 5개 모두 리렌더링 됨 */} {data.split(" ").map((el) => ( <Word key={uuidv4()} el={el} /> ))} {/* 1. memo시 key 또는 el이 변경된 부분만 리렌더링 됨(즉, THERE은 제외! */} {/* {data.split(" ").map((el, index) => ( <Word key={index} el={el} /> ))} */} <button onClick={onClickChange}>CHANGE</button> </> ); }
- child.tsx
import { memo } from "react"; interface IWordProps { el: string; } function Word(props: IWordProps): JSX.Element { console.log("child 렌더링 됨", props.el); return <span>{props.el}</span>; } export default memo(Word);
▼ key값을 uuid로 설정시 문제점
uuid를 사용하면 memo를 걸어놔도 key값이 변경되어 props로 넘어가기 때문에 변경된 부분이 모두 리렌더링 된다.
uuid는 불필요한 리렌더링을 초래하므로 필요한 상황에서만 주의 해서 사용할 것!