[React] 5가지 Hooks

안셩·2024년 8월 21일

복습내용

목록 보기
13/27
post-thumbnail
  1. usestate : 컴포넌트 내부에서 state 변경의 위해 사용.
  2. useEffect : 컴포넌트가 렌더링 된 이후마다 특정 작업을 수행하도록 설정할 수 있는 Hook (e.g. 서버에서 데이터 가져오기)
  3. useRef : 리렌더링과 상관없이 useState와 같이 특정 값을 저장하기 위해 사용
  4. useContext : prop drilling이 많아져 복잡해졌을 때, 전역상태를 관리할 수 있는 Hook (꼭 전역상태가 아니여도 됨.)
  5. memoization : 불필요한 렌더링이 발생하지 않도록 최적화하는 방법

1. useState

  • state: 컴포넌트 내부에서 바뀔 수 있는 값
  • state의 변경을 위해 useState()를 쓴다.
const [ state, setState ] = useState( 초기값 );

일반 업데이트 방식 vs 함수형 업데이트 방식

  • 일반 업데이트 방식: setState가 각각 실행되는 것이 아니라, 리액트는 명령을 하나로 모아 마지막 하나만 실행
onClick={() =>{
	setNumber(number + 1);
    setNumber(number + 1);
    setNumber(number + 1);
    }
  • 함수형 업데이트 방식: 3번을 동시에 명령내리면, 그 명령을 모아 순차적으로 각각 1번씩 실행시킴.
onClick={() =>{
	setNumber((prev) => prev + 1);
    setNumber((prev) => prev + 1);
    setNumber((prev) => prev + 1);
    }

=> 왜 setCount 이후에 콘솔로그로 count 찍으면 반영이 안되는지 답안: setState(상태변경함수) 실행은 일반업데이트 시 비동기적으로 동작(배치업데이트(batch update))


2. useEffect

컴포넌트가 렌더링 된 이후마다 특정 작업을 수행하도록 설정할 수 있는 Hook

((보통 외부 데이터를 불러올 때 사용해요. (e.g. 서버에서 데이터 가져오기))

의존성 배열(dependency array)

: "이 배열에 값을 넣으면 그 값이 바뀔 때만 useEffect를 실행할게"

useEffect(()=>{
	// 실행하고 싶은 함수
}, [의존성배열])
  • 의존성배열이 '빈 배열'일 경우: 어떤 함수를 컴포넌트가 렌더링 된 후 단 한번만 실행
  • 의존성 배열에 '값이 있는' 경우: 값이 바뀔때마다 useEffect 실행

3. useRef

useState와 더불어 특정 값을 저장하기 위해 사용, 리렌더링과 상관없이 값을 기억하기 위해 사용.

(자바스크립트 DOM API를 직접사용하지 않고 DOM 요소를 다루기 위한 용도로도 자주 사용)

const ref = useRef("초기값");

console.log("ref", ref); // ref > {current: '초기값'}

값을 변경할 수도 있음.

state vs ref

  • state: 리렌더링이 꼭 필요한 값을 다룰 때 사용(리렌더링되면 내부 변수들이 초기화됨)
  • ref: 리렌더링을 발생시키지 않는 값을 저장할 때 사용(내부 변수들의 초기화를 막음)

4. useContext

prop drilling이 많아지면 prop이 어떤 컴포넌트로부터 왔는지 파악이 어려워져, 전역 컴포넌트를 관리할 수 있는 Hook

  • createContext : context를 생성합니다.
  • useContext : context를 구독하고 해당 context의 현재 값을 읽습니다.
  • Provider : context를 하위 컴포넌트에게 전달합니다.
  • 리렌더링 문제 신경써줄 것!
    useContext를 사용할 때, Provider에서 제공한 value가 달라진다면 useContext를 사용하고 있는 모든 컴포넌트가 리렌더링 됩니다. 따라서 value 부분을 항상 신경써줘야 해요! -> memoization이 해결해 줌.

예제. GrandFather.jsx -> (Father.jsx) -> Child.jsx로 prop 전달

1) context 폴더 > context.js 파일 생성

import { createContext } from "react";

export const FamilyContext = createContext(null); // null은 context의 초기값

2) GrandFather.jsx 파일에 provider로 감싸줄 것.

import React from "react";
import Father from "./Father";
import { FamilyContext } from "../context/FamilyContext";

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

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

export default GrandFather;

3) Father.jsx 파일에 props 제거

import React from "react";
import Child from "./Child";

function Father() {
  return <Child />;
}

export default Father;

4) Child.jsx 수정

import React, { useContext } from "react";
import { FamilyContext } from "../context/FamilyContext";

function Child({ houseName, pocketMoney }) {
  const stressedWord = {
    color: "red",
    fontWeight: "900",
  };

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

  return (
    <div>
      나는 이 집안의 막내에요.
      <br />
      할아버지가 우리 집 이름은 <span style={stressedWord}>{data.houseName}</span>
      라고 하셨어요.
      <br />
      게다가 용돈도 <span style={stressedWord}>{data.pocketMoney}</span>원만큼이나
      주셨답니다.
    </div>
  );
}

export default Child;

5. memoization

불필요한 렌더링이 발생하지 않도록 최적화(optimization)하는 방법

  • memo(React.memo) : 컴포넌트를 캐싱
  • useCallback : 함수를 캐싱
  • useMemo : 을 캐싱

1) memo(React.memo)

export default React.memo(Box1);
export default React.memo(Box2);
export default React.memo(Box3);
  • 최초 렌더링 이외에는 App.jsx 컴포넌트의 state가 변경되더라도 자식 컴포넌트들은 렌더링이 되지 않는다.
  • React.memo를 이용해서 컴포넌트를 메모리에 저장해두고 필요할 때 갖다 쓰게 된다. (= 컴포넌트 memoization)

2) useCallback

: 인자로 들어오는 함수 자체를 기억(= 함수 memoization)

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

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

3) useMemo

: 동일한 값을 반환하는 함수를 계속 호출해야 하면 필요없는 렌더링을 함. 맨 처음 해당 값을 반환할 때 그 값을 특별한 곳(메모리)에 저장해 필요할 때 마다 다시 함수를 호출해서 계산하는게 아니라 이미 저장한 값을 단순히 꺼내와서 쓸 수 있다.

// as-is
const value = 반환할_함수();

// to-be
const value = useMemo(()=> {
	return 반환할_함수()
}, [dependencyArray]);
  • 특히 복잡한 계산 결과값을 memoization할 때 많이 사용
  • useMemo를 남발하게 되면 별도의 메모리 확보를 너무나 많이 하게 되기 때문에 오히려 성능이 악화될 수 있습니다. 필요할 때만 쓰기로 합시다 🙂
profile
24.07.15 프론트엔드 개발 첫 걸음

0개의 댓글