React

[React] Hooks (useState, useEffect, useRef, uesContext, uesMemo)

이경욱 2023. 11. 9. 20:55

0. 기본 배경

(1) 리-렌더링의 발생 조건

A. 컴포넌트에서 state가 바뀌었을 때

B. 컴포넌트가 내려받은 props가 변경되었을 때

C. 부모 컴포넌트가 리-렌더링 된 경우 자식 컴포넌트는 모두

 

(2) 최적화

리액트의 훅을 사용하면서

필요한 부분 외적으로 렌더링이 일어나는 것은

비효율이 발생하는 것이기 때문에

최대한 줄여야한다.

 

 

1. useState

가장 기본적으로 사용되며,

함수형 컴포넌트 안에서 가변적인 상태를 갖게 한다.

 

가변적인 상태란?

변경사항을 해당 부분만 업데이트 시킬 수 있는 것

 

// 사용 문법
const [state, setState] = useState(초기값);

 

 

(1) 함수형 업데이트

기존의 변경할 값만 넣는 것이 아닌,

내부에 콜백함수를 줄 수 있다.

 

인자 부분에 현재 State를 가져올 수 있고

로직 부분에 변경할 값을 줄 수 있다.

setState((현재 State) => {변경할 코드})

 

 

그럼 기존 코드 방식과 차이는 무엇일까?

구분 기존 setState 함수형 업데이트
업데이트 배치 (모아서 하나만) 각각 순차적으로 진행
     

 

 

이렇게 설계한 이유는?

'업데이트의 효율성을 위해'

 

웨이터는 손님의 주문 하나마다 주방으로 가지 않는다.

모든 주문을 다 듣고 주방에 넘겨주게된다.

 

이처럼 기존 setState는

효율적인 업데이트를 위해

배치형식으로 처리되도록 설계하였다. 

 

 

 


2. useEffect

useEffect란?

렌더링 시 특정한 작업이 일어나야 할 때 설정하는 Hook

 

(1) 발동 조건

  • 컴포넌트가 화면에서 보여질 때
  • 컴포넌트가 화면에서 사라졌을 때 (return)

 

(2) 기본 문법

useEffect(callbackFn, [DependencyList])

 

* useState가 많은 컴포넌트 같은 경우 렌더링 빈도가 잦으므로

useEffect Hook도 의도하지 않은 상황에서 많이 나타날 수 있다.

이를 방지하기 위해 '의존성 배열' 이라는 조건을 부여한다.

 

의존성 배열 (dependency array)이란?

이 배열에 값을 넣으면 해당 배열 내부의 값이 바뀔 때에만 useEffect를 실행하는 것

 

 

(3) clean up

컴포넌트가 화면에서 사라질 때

useEffect를 발동시키는 것이 clean up이라고 부른다.

 

callbackFn 내부 로직에서 함수를 retrun해주면 clean up을 할 수 있다.

// 기본 문법
useEffect(() => {
return () => {clean up 로직 작성});

 

 

 


3. useRef

DOM 요소에 접근할 수 있는 Hook

 

(1) JavaScript vs React

기존 방식 (JavaScript)

const divTag = document.getElementById('')
const pTag = document.querySelector('')

 

 

useRef (React)

fuction App()
	const ref = useRef("초기값")
	// -> current : '초기값'
    
	ref.current = "변경값"
    // -> current : '변경값'

 

이렇게 current라는 key를 가진 객체 형태로 반환되는 것을 알 수 있다.

해당 key를 호출하여 value 값을 변경할 수도 있다.

 

 

 

 

(2) 사용 방식

 

A. 저장공간

(a) state와 비슷한 역할.

state는 값이 변경되면 렌더링되면서 내부 변수가 초기화 된다.

 

하지만 useRef는 렌더링을 하지 않는다.

렌더링이 되어도 unmount (컴포넌트가 사라지기) 전까지 값을 유지한다.

 

따라서,

State는 렌더링이 필요한 값에 사용

Ref는 렌더링을 하지 않을 값을 저장

 

(b) DOM

렌더링이 되자마자

특정 태그가 focusing 돼야할 경우 useRef를 사용하면 된다.

 

function App() {
  const idRef = useRef("");

  // 렌더링이 될 때
  useEffect(() => {
    idRef.current.focus();
  }, []);

 

useEffect로 Dependency Array에 빈 배열을 주면

최초 렌더링이 되었을 때에만 동작하는데,

 

이때 focus() 라는 메서드가 있어서

해당 기능을 사용하면 

특정 태그를 focus 할 수 있다.

 

 

4. useContext

부모 => 자식 컴포넌트로 데이터를 넘겨줄 때

props를 넘겨주었다.

depth가 깊어지게 되면 drilling 현상이 일어나기 때문에

useContext로 전역 데이터를 관리할 수 있게된다.

 

(1) 사용 방법

 

A. 개념

a. createContext : context 생성

b. Consumer : context 변화 감지

c. Provider : context 전달 (to 하위 컴포넌트)

 

 

B. 문법

● useContext 파일 (createContext)

// context폴더 > FamilyContext.js 생성

import { createContext } from "react";

export const FamilyContext = createContext(null);

 

  데이터를 제공하는 컴포넌트의 문법 (Provider)

import { FamilyContext } from "../context/FamilyContext"; //useContext 파일 import

function GrandFather() {
  const houseName = "스파르타";
  const pocketMoney = 10000;

  return (
    <FamilyContext.Provider value={{ houseName, pocketMoney }}>
      <Father />
    </FamilyContext.Provider>
  );
}

 

  데이터를 받아오는 컴포넌트의 문법

import { FamilyContext } from "../context/FamilyContext"; //useContext 파일 import

function Child({ houseName, pocketMoney }) {

  const data = useContext(FamilyContext);
  console.log("data", data);
}

 

* useContext를 사용할 때

Provider에서 제공한 value가 달라진다면

useContext를 사용하고 있는 모든 컴포넌트가 리렌더링 되기 때문에

비효율이 발생할 수 있다.

 

이 부분을 극복하기 위해

'메모이제이션' 이라는 방식을 사용하게 된다.

 

 

 

5. 최적화 (React.memo, useCallback, useMemo)

캐싱이란?

메모리에 일시적으로 저장하는 것

 

(1) React.memo : 컴포넌트를 캐싱 

부모 컴포넌트의 state 가 porps 변경이 아니라면

리렌더링은 되지 않는다. 

 

기본 문법

export default React.memo(Box1);

 

이렇게 하위 컴포넌트에서 export할 때 React.memo() 로 감싸주기만 하면 된다.

 

 

(2) useCallback : 함수를 캐싱

useCallback을 통해 함수 자체를 저장하고

특정 조건 이외에는 변경되지 않도록 하는 메서드

 

사용 방법

// 변경 전
const initCount = () => {
  setCount(0);
};

// 변경 후
const initCount = useCallback(() => {
  setCount(0);
}, []);

 

useCallback을 넣어주고 Dependency Array 를 함께 넣어주면 된다.

 

 

 

(3) useMemo : 값을 캐싱

함수가 retrun하는 값을 저장한다.

 

사용 방법

// 기존 코드
const value = 반환할_함수();

// useMemo
const value = useMemo(()=> {
	return 반환할_함수()
}, [dependencyArray]);

 

 

객체, 함수 등은 리렌더링이 될 때

새로운 주소값을 가져오게 된다.

따라서 useEffect로 객체를 가져오면

다른 곳이 리렌더링이 되었을지라도

주소값이 바뀌며 값이 바뀌었다고 인식하게되어

useEffect가 실행된다.

 

이러한 문제점을 방지하기 위해

useMemo를 통해 값을 저장하고

해당 값이 바뀌었을 때만 인식하도록

사용할 수 있다.