[React] Hook 간편 설명서

MOON HEE·2022년 6월 11일
0
post-thumbnail

1. useState()


1-1. useState() 사용법

const [변수명, set함수명] = useState(초기값);

useState는 비동기적으로 실행하기 때문에 뒤의 코드가 먼저 혹은 동시에 실행 될 여지가 있어서, 순서대로 실행되어야 한다면 useEffect를 사용한다.

1-2. useState() 예제

import React, { useState } from "react";

function Counter(props) {
  const [count, setCount] = useState(0);
  
  return (
  	<div>
      <p>{count}번 클릭했습니다.</p>
      <button onClick={() => setCount(count + 1)}>
      	클릭
      </button>
    </div>  
  );
}

export default Counter;

1-3. CodeSandbox 화면


2. useEffect() : 변수 변경여부를 감시하는 방법


Class Component의 생명주기처럼 작동한다.
기본적으로 이펙트 함수는 '처음 렌더링된 이후'와 '업데이트로 인한 재렌더링 이후'에 실행된다.

2-1. 사용법

// 형식
useEffect(이펙트 함수, 의존성 배열);
// 이렇게 자주 사용합니다
useEffect(() => {
	// 컴포넌트가 마운트 된 이후,
  	// 의존성 배열에 있는 변수들 중 하나라도 값이 변경되었을 때 실행됨
  	// 의존성 배열에 빈 배열([])을 넣으면 마운트와 언마운트시에 단 한번씩만 실행됨
  	// 의존성 배열 생략 시 컴포넌트 업데이트 시마다 실행됨
  ...
  return () => {
    	// 컴포넌트가 마운트 해제되기 전에 실행됨
    	...
  }
  
}, [의존성 변수1, 의존성 변수2, ...]);

2-2. 예제

ComponentDidMount()와 ComponentDidUpdate() 역할

import React, { useState, useEffect } from "react";

function Counter(props) {
  const [count, setCount] = useState(0);
  
  // document의 title을 업데이트한다(의존성배열 생략)
  useEffect(() => {
  	document.title = `You clicked ${count} times`;
  }); 
  
  return (
  	<div>
      <p>{count}번 클릭했습니다.</p>
      <button onClick={() => setCount(count + 1)}>
      	클릭
      </button>
    </div>  
  );
}

export default Counter;

2-3. CodeSandbox 화면

2-4. useLayoutEffect와 비교

import { useState, useEffect, useLayoutEffect } from 'react'

function App() {
  const [value, setValue] = useState(100);
  
// 커져랏 버튼을 누를 때마다 1000px로 갔다가 300px로 바뀜
console.log(value)
  useEffect(() => {
    if (value >= 1000) {
      setValue(300);
        console.log(value)

    }
  }, [value]);

// 커져랏 버튼을 누를 때마다 그려지기 전에 300px로 정해짐
console.log(value)
// useLayoutEffect(() => {
//     if (value >= 1000) {
//         setValue(300);
//         console.log(value)
//     }
//   }, [value]);

  return (
    <div>
      {/* <div style={{ width: value, height: value, backgroundColor: 'blue', transition: '1s all' }}></div> */}
      <div style={{ width: value, height: value, backgroundColor: 'blue' }}></div>
      <button onClick={() => {setValue(1000)}}>커져랏!</button>
      <button onClick={() => {setValue(200)}}>작아져랏!</button>
    </div>
  )
}

export default App;

useEffect vs useLayoutEffect에 관해서 더 알고 싶으면 여기 블로그가 도움이 됩니다.


3. useMemo() _무거운 함수 재렌더링 안되게하는 방법


3-1. 사용법

// useMemo는 콜백함수와 의존성배열을 인자로 받는다
const memoizedValue = useMemo(
	() => {
      	// 연산량이 많은 작업을 수행하여 결과를 반환
      	return computeExpensiveValue(의존성 변수1, 의존성 변수2);
    }, 
  	[의존성 변수1, 의존성 변수2]
);
  • Memoized value를 리턴하는 Hook
  • `의존성 배열`을 넣고 해당 변수의 값이 바뀔 때마다 새로 값을 계산할 경우 사용한다.
    • 의존성 배열을 넣지 않을 경우, 매번 create함수가 실행되기 때문에 매 렌더링마다 함수가 실행되므로 의미가 없다.
    • 의존성 배열이 빈 배열일 경우, 컴포넌트 마운트 시에만 호출된다. 따라서 마운트 이후에는 값이 변경되지 않는다. 마운트 시점에만 값을 계산할 필요가 있을 경우 사용 가능할 것이다.

3-2. 필요성

예제 풀이 유튜브 바로가기

❗ useMemo 사용 전
렌더링 ▶ Component함수 호출 ▶ 모든 내부 변수 초기화

import React from "react";

function Component() {
  const value = calculate();
  return <div>{value}</div>;
}

function calculate() {
  return 10;
}

export default Component;

만약 위 코드에서 calculate()가 무겁다면 비효율적일 것이다. calculate()는 무의미하게 value라는 변수에 반복적으로 같은 값을 할당하기 때문이다. 이럴 때 useMemo를 통해 Memoization을 해줄 수 있다.


❗ useMemo 사용 후
렌더링 ▶ Component함수 호출, Memoization ▶ 렌더링 ▶ Component함수 호출, Memoize된 값을 재사용

import React, { useMemo } from "react";

function Component() {
  const value = useMemo(() => calculate(), []);

  return <div>{value}</div>;
}

function calculate() {
  return 10;
}

export default Component;

calculate를 다시 호출하지 않고 메모리에서 꺼내와서 재사용한다.


아래 예제는 for문으로 의도적으로 시간지연을 줘서 useMemo 적용 전후를 비교하는 것이다. hardSum 변수를 useMemo로 주면 easySum은 더이상 시간지연이 일어나지 않게 된다.
import React, { useState, useMemo } from "react";

const hardCalculate = (number) => {
    console.log("어려운 계산!");
    for (let i = 0; i < 999999999; i++) {}
    return number + 10000;
};

const easyCalculate = (number) => {
    console.log("짱 쉬운 계산!");
    return number + 1;
};


function App() {
    const [hardNumber, setHardNumber] = useState(1);
    const [easyNumber, setEasyNumber] = useState(1);

// const hardSum = hardCalculate(hardNumber);
const hardSum = useMemo(() => {
    return hardCalculate(hardNumber)
}, [hardNumber])
    const easySum = easyCalculate(easyNumber);

    return (
        <div>
        <h3>어려운 계산기</h3>
        <input
            type="number"
            value={hardNumber}
            onChange={(e) => setHardNumber(parseInt(e.target.value))}
        />
        <span> + 1 = {hardSum}</span>

        <h3>쉬운 계산기</h3>
        <input
            type="number"
            value={easyNumber}
            onChange={(e) => setEasyNumber(parseInt(e.target.value))}
        />
        <span> + 1 = {easySum}</span>
        </div>
    );
}

export default App;

3-3. useMemo vs useEffect 비교

useMemo

useMemo(실행될 것, [값])라고 할 때 useMemo는 렌더링 전에 실행되어 메모이제이션한 것을 반환해준다. 값이 바뀌었는지 확인하고 렌더링 전에 저장된 것을 보내주는 것이다.

useEffect

반면 useEffect는 렌더링 후에 일어난다. 렌더링 후에 상태가 업데이트 되었을 때를 감지하여 동작한다.

import React, { useState, useEffect } from "react";

function App() {
    const [number, setNumber] = useState(0);
    const [isKorea, setIsKorea] = useState(true);
    
    // 삼항연산자 isKorea가 참이면 '한국', 거짓이면 '외국'
    const location = isKorea ? '한국' : '외국';

    // useEffect 추가해서 location이 바뀔 때만 실행(최초 렌더링 시 1회 실행됨)
    useEffect (() => {
        console.log('useEffect 호출')
    }, [location]);

    // ✅location이 원시타입(string)이 아니라, 오브젝트라면? -> useMemo 사용(다음 코드로 확인하세요)

    return (
        <div>
            <h2>하루에 몇끼 먹어요?</h2>
            <input 
                type="number" 
                value={number}
                onChange={(e) => setNumber(e.target.value)}
            />
            <hr />
            <h2>어느 나라에 있어요?</h2>
            <p>나라: {location}</p>
            <button onClick={() => setIsKorea(!isKorea)}>비행기 타자</button>
        </div>
    );
}

export default App;
import React, { useState, useEffect, useMemo } from "react";

function App() {
    const [number, setNumber] = useState(0);
    const [isKorea, setIsKorea] = useState(true);
    
    // 삼항연산자 isKorea가 참이면 '한국', 거짓이면 '외국'
    // 오브젝트인 경우 다른 메모리상 공간에 저장된다.
    // 계속 주소가 바뀌므로 useEffect가 계속 호출된다.
    // const location = {
    //     country: isKorea ? '한국' : '외국';
    // }

    const location = useMemo(() => {
        return {
            country: isKorea ? '한국' : '외국',
        };
    }, [isKorea]);

    // useEffect 추가해서 location이 바뀔 때만 실행(최초 렌더링 시 1회 실행됨)
    useEffect (() => {
        console.log('useEffect 호출')
        // 뭔가 오래걸리는 작업..
    }, [location]);

    return (
        <div>
            <h2>하루에 몇끼 먹어요?</h2>
            <input 
                type="number" 
                value={number}
                onChange={(e) => setNumber(e.target.value)}
            />
            <hr />
            <h2>어느 나라에 있어요?</h2>
            <p>나라: {location.country}</p>
            <button onClick={() => setIsKorea(!isKorea)}>비행기 타자</button>
        </div>
    );
}

export default App;

4. useCallback()


4-1. 사용법

useMemo()와 유사하지만 값이 아닌 함수를 반환한다.

const memoizedCallback = useCallback(
	() => {
      	doSomething(의존성 변수1, 의존성 변수2);
    }, 
  	[의존성 변수1, 의존성 변수2] // 의존성 배열
);

5. useRef() _리액트에서 DOM 접근하는 방법


5-1. 사용법

  • useRef()는 Reference를 사용하기 위한 Hook이다.
  • Reference란 특정 컴포넌트에 접근할 수 있는 객체를 의미한다. useRef()은 이 Reference를 반환한다.
  • 또한 useRef()은 내부의 데이터가 변경되었을 때 별도로 알리지 않는다. 재렌더링하지 않는다.
  • 참고하면 좋은 블로그입니다.
const refContainer = useRef(초기값);

5-2. 예제

import React, { useRef } from "react";

function TextInputWithFocusButton(props) {
  const inputElem = useRef(null);

  const onButtonClick = () => {
    // `current`는 마운트된 input element를 가리킴
    inputElem.current.focus();
  };

  return (
    <>
      <input ref={inputElem} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}

export default TextInputWithFocusButton;

profile
자고 일어나면 해결되는게 더 많은 듯 그럼 잘까

0개의 댓글