Notice
Recent Posts
Recent Comments
«   2024/06   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30
Tags more
Archives
Today
Total
관리 메뉴

HYEWON JUNG의 개발일지

React Hooks useContext, reactMemo, useCallback, useMemo 본문

React

React Hooks useContext, reactMemo, useCallback, useMemo

혜won 2023. 11. 9. 02:20

useContext (useContext API)

context 는 전역적으로 사용되는 어떤것을 말할 때 많이 쓰이는 용어다. 

일반적을 데이터를 넘겨줄 때 부모에서 자식컴포넘트로 props를 통해서 데이터를 내려주곤 했는데 이게 너무 많이 되면 props drilling이 일어난다고 했었다. 

 

그래서 데이터를  효율적으로 전달할 수 있도록 useContext를 사용하는 것이다.

useContext의 필수 개념

3개를 꼭 기억해야한다. 

  • createContext = 객체, context 생성
  • consumer = context 변화 감지
  • Provider = context를 하위컴포넌트로 전달

useContext를 사용하기 위해서는 js파일을 하나 만들고 

import { createContext } from "react";

export const FamilyContext = createContext(null);

이렇게 createContext를 import 해주고 export로 현재 파일에서 createContext의 초기값은null로 주면 우선은 전역에서 원하는 하위 컴포넌트들이 접근할 수 있게 만들었다. 

 

정보를 주는 컴포넌트에서 정보를 넣는 방법은 

function GrandFather() {
    const houseName= "sparta"
    const pocketMoney = 10000;
  return (
    <FamilyContext.Provider value={{
        houseName: houseName,
        pocketMoney,
    }}>
        <Father/>
    </FamilyContext.Provider>
  )
}

 

<FamilyContext.Provicer>사이에 넣어주고 value로 객체에 정보를 담아주 전달하면된다. 그후에 father 컴포넌트는 전달할 것도 받을 것도 없이 진행하고 child에서 정보를 받는 법은

function Father( ) {
    
  return <Child />
}

 

function Child() {
   const data = useContext(FamilyContext);
  return (
    <div>
        나는 <span style={style}>{data.houseName}</span>집안의 막내에요.<br/>
        할아버지가 나에게 용돈<span style={style}>{data.pocketMoney}</span>원 을 주셨어요
    </div>
  )
}

 

이렇게 하면 

이렇게 props로 내려준 것과 같은 결과를 낸다. 하지만 useContext는 props로 전달 받은 것이 아닌 context로서 전역에서 가져온 것을 기억해야한다.

 

하지만 context를 너무 남발하면 정보를 준 상위 컴포넌트의 context에 준 value가 변하게 되면 useContext로연결되어있는 모든 하위 컴포넌트들이 리렌더링 되는 비효율이 있다 . 그것을 해결하는 것이 아래의 메모리제이션이다. 

메모리제이션

react.Memo - 컴포넌트에 대한 메모리 제이션

useCallback - 함수에 대한 메모리 제이션

useMemo - value에 대한 메모리 제이션 value는 함수가 리턴하는 값 또는 그냥 값 자체

 

React Memo

context에서도 그렇고 기본적으로 상위 컴포넌트가 리렌더링 되면 하위 컴포넌트들고 다 리렌더링이 되는데 상위 컴포넌트에게 전달 받는 것도 없는 하위 텀포넌트들은 굳이 리렌더링이 될 이유가 없으니 전달 받는 정보가 없는 하위 컴포넌트들이 리렌더링 되지 않게 하는 것이 react memo이다. 

count앱을 만들어 보면

 

원래라면 box3까지 렌덜이이 되야하지만 memo기능을 이용해서 App컴포넌트만 렌더링 되게 했다 컴포넌트를 메모리제이션하는 것이다.

 

사용법은 정말 간단하게 

export default React.memo(컴포넌트 이름)

만 붙여주면 된다. 그렇게 되면 하위 컴포넌트들이 렌더링이 되지 않는것을 볼 수 있다.

 

useCallback

useCallback은 인자로 들어오는 함수 자체를 메모리 제이션 한다. count를 초기화하는 버튼은 box1에 넣고 props로 함수를 내려줘서 실행되게 하면 box1은 렌더링이 될까 안될까

 

카운트 버튼을 할 대마다 box1도 같이 렌더링 되는 것을 확인할 수 있다. box1이 받은 props는 변한게 없는데 왜 리렌더링이 되었을까? 

이유는 데이터는 props가 바꼈다고 생각하기 때문이다. 

 

그래서 이것을 무효화하는 hook이 바로 useCallback이다.

//as-is
const initCount =()=>{
    setCount(0)
  }
  //를 아래처럼 적어준다
//to-be  
    const initCount =useCallback(()=>{
    setCount(0)
  },[])

함수 자체를 useCallback으로 감싸주고 두번째인자로 의존성 배열을 넣어준다.  의존성 배열은 왜 있는 걸까? 그 이유는  만약에 우리가 초기화 버튼을 눌렀을 때 count값을 출력하면 무조건 0으로 찍히게 된다. 하지만 우리가 count값을 써야한다면 이것을 조건적으로 렌더링이 되는 기준으로 삼아야한다

[count]를 하게되면 count가 변하는 때에만 렌더링이 될 수 있똘고 조건을 거는 것이다. 

지금 보기엔 아까 useCallback쓰기 전과 다를 것이 없어 보이겠지만 로직들이 더 많아지고 기능이 많아진다면 그 기능을 할 때마다 리렌더링이 된다면 손해이니 어떤 특정한 기능을 할 때만 렌더링이 되도록하는 것이다.

useMemo

동일한 값을 변환하는 함수를 계속호출 한다면 필요없는 렌더링이 반복되고 만약 그 함수가 엄청 무거운 작업이라면 렌더링 시간이 늦어지게 된다. 계속 같은 값을 내는 함수라면 그 값을 기억했다 이미 저장한 값만 꺼내와 쓸 수 있게 한다. = 케싱을 한다

useMemo 사용법

// as-is
const value = 반환할_함수(); //반환값 아니면 그냥 값

// to-be
const value = useMemo(()=> {
	return 반환할_함수() //반환에 함수 반환값 함수()넣기 아니면 그냥 값
}, [dependencyArray]);

 

예제로 해보면 

function HeavyComponent() {
    const [count, setCount] = useState(0)
    const heavywork =()=>{
        for(let i = 0; i<4000000000; i++){}
            return 100; 
    }
    const value = heavywork();
    console.log(`value는 ${value}입니다.`)
  return (
    <>
        <p>나는 엄청 무거운 컴포넌트야</p>
        <button onClick={()=>{setCount(count+1)}}>누르면 아래 카운트가 올라가요</button>
        <p>{count}</p>
    </>
  )
}

이렇게 for문이 엄청 길게 된다면 리렌더링이 될때마다 로딩시간이 길어지게된다. 버튼을 누르고 카운트가 올라가는 속도가 엄청 느려지게 된다. 이때 heavywork의 호출에 useMemo를 쓴게 된다면 항상 100을 리턴라니 100의 값을 기억하고 있는거죠. 

function HeavyComponent() {
    const [count, setCount] = useState(0)
    const heavywork =()=>{
        for(let i = 0; i<4000000000; i++){}
            return 100; 
    }
    const value = useMemo(()=>heavywork(),[]);
    console.log(`value는 ${value}입니다.`)
  return (
    <>
        <p>나는 엄청 무거운 컴포넌트야</p>
        <button onClick={()=>{setCount(count+1)}}>누르면 아래 카운트가 올라가요</button>
        <p>{count}</p>
    </>
  )
}

 

이렇게 해주면 처음 렌덜이하는 부분만 로딩이 오래걸리고 그 후부터는 빠르게 진행되는 것을 확인할 수 있다.  조금더 자세히 알 수 있는 예제로 

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>
    </>
  );
}

 

useMemo를 사용한 예제인데 useMemo를 사용한 이유는 useEffect가 me를 의존성 배열로 잡고 있는데 숫자가 올라가는 버튼 클릭시  me가 변하지 않을 것 같은데 계속 리렌더링이 되면서 console이 찍히기 때문인데 이를 방지해주기 위해서 값인me를 memo로 저장해두고 isalive가 바뀔 때만 me가 리렌더링이 되게 한 로직입니다. 

 

me가 바뀌었다고 인식되는 순서

위 예제에서 버튼이 선택돼서 uselessCount  state가 바뀌게 되면

리렌더링이 되죠

→ 컴포넌트 함수가 새로 호출됩니다

→ me 객체도 다시 할당해요(이 때, 다른 메모리 주소값을 할당받죠)

→ useEffect의 dependency array에 의해 me 객체가바뀌었는지 확인해봐야 하는데

→ 이전 것과 모양은 같은데 주소가 달라요!

리액트 입장에서는 me가 바뀌었구나 인식하고 useEffect 내부 로직이 호출됩니다.

 

이와 같이 useMemo는 같은 값을 가지는  값이나 함수() 가 리렌더링되어 주소값이 변경되는 것을 막아주는 기능이다

 

'React' 카테고리의 다른 글

Redux  (0) 2023.11.10
component LifeCycle 생명주기  (1) 2023.11.09
useState, useEffect, useRef  (0) 2023.11.08
GlobalStyle, sass, css초기화  (0) 2023.11.08
styled component 작성법 장점 자바스크립트 메소드 사용  (0) 2023.11.07