[React] React Hook

Kim Yuhyeon·2024년 3월 5일

React

목록 보기
6/10

Hook


Hook을 사용하여 모든 컴포넌트까지(함수형?) 독립적으로 재사용 가능하다.

종류

기본

  • useState: 상태를 사용함
  • useEffect: 상태가 변화할때 실행됨.
  • useRef: 변경 가능한 ref객체 생성
  • useContext: 컨텍스트 값을 사용함

성능 관련

  • useMemo : 상태가 변화할 때 계산
  • useCallback : 함수가 변화할 때 재정의

useState

관리할 상태를 초기화

  • ex. CountComponent.js

    import React, { useState } from "react";
    
    export default function CountComponent() {
     const [count, setCount] = useState(0)
    
     const addCount = () => {
       setCount(count+1);
     }
    
     return (
       <div>
       <div>{count}</div>
       <button onClick={addCount}>1 증가</button>
    </div>
    )
    }```

useEffect(effectCallback:Fn, deps:Array)

  • 컴포넌트가 mount(초기 Rendering) 되거나

  • 업데이트 될 때 (State, props 등이 변경시) 실행할 함수 정의 (보통 서버에 데이터 요청을 처리함)

    • 업데이트될 때 = deps(dependency?) 에 들어가는 값이 변경될때
  • ex. CountComponent.js

    import React, { useEffect, useState } from "react";
    
    export default function CountComponent() {
      const [count, setCount] = useState(0)
    
      const addCount = () => {
        setCount(count+1);
      }
    
      useEffect(() => {
        console.log("데이터받아오기! 이 함수는 한번만 실행됩니다.");
        return () => {
          console.log("메모리를 잡아먹으면 리소스를 해제하는 함수를 return해주어야 합니다.")
        }
      }, [])
    
      // count가 바뀔때마다 다시 호출할게용 
      useEffect(() => {
        console.log(`카운트가 증가할 때마다 실행 \n -count: ${count}`)
      }, [count])
      return (
        <div>
        <div>{count}</div>
        <button onClick={addCount}>1 증가</button>
    </div>
    )
    }
  • App.js

import logo from './logo.svg';
import './App.css';
import HelloWorld from './components/HelloWorld';
import CaptionImage from './components/CaptionImage';
import BlinkCompnent from './components/BlinkComponent.js';
import CountComponent from './components/CountComponent.js';
import { useState } from 'react';


  function App() {
    const [visible, setVisible] = useState(false)

    return (
      <div className="App">
        {/* visible 상태에 따라서 CountComponent 보였다 안보였다 */}
        <button onClick={() => setVisible(!visible)}>보이기</button>
        {visible? <CountComponent/> : null}
      </div>
    );
  }
  export default App;

useRef()

다른 컴포넌트를 참조하여햐 할 때

  • RegisterInputButton.js

       import React, {useEffect, useRef, useState} from 'react'
    
       export default function FocusInputButton({text}) {
    
           const inputRef = useRef();
    
           const focusInput = () => {
               inputRef.current.focus();
           };
    
           return (
               <div>
                   <button onClick={focusInput}> 입력하러 가기 </button>
                   <div style={{height:2000}}></div>
                   <input ref={inputRef} type="text"/>
                   <div style={{height:2000}}></div>
    
               </div>
           )
       }
  • App.js

import logo from './logo.svg';
import './App.css';
import HelloWorld from './components/HelloWorld';
import CaptionImage from './components/CaptionImage';
import BlinkCompnent from './components/BlinkComponent.js';
import CountComponent from './components/CountComponent.js';
import { useState } from 'react';
import FocusInputButton from './components/RegisterInputButton.js';


function App() {
  const [visible, setVisible] = useState(false)
  
  return (
    <div className="App">
      {/* visible 상태에 따라서 CountComponent 보였다 안보였다 */}
      {/* <button onClick={() => setVisible(!visible)}>보이기</button>
      {visible? <CountComponent/> : null} */}

      <FocusInputButton/>
    </div>
  );
}
export default App;

useMemo(memoCallback:Fn, deps:Array)

값을 메모이제이션 (잠깐 저장)

useMemo 사용하지 않았을 때

  • PrimeCalculator.js
import React, {useState, useMe} from "react";


function calculatePrimes(limit) {
    console.log(`limit: ${limit}에 대한 소수 계산`)
    const primes = [];
    for(let i=2; i<=limit; i++) {
        let isPrime = true;
        for(let j=2; j<i; j++) {
            if (i % j == 0) {
                isPrime = false;
                break;
            }
        }
        
        if (isPrime) {
            primes.push(i);
        }
    }

    return primes;
}

export default function PrimeCalculator(props) {
    const [limit, setLimit] = useState(10);
    const primes = calculatePrimes(limit);

    return (
        <div>
            <input type="number" value={limit} onChange={(e) => setLimit(Number(e.target.value))}/>
            <p>계산된 소수 : {primes.join(', ')}</p>
        </div>
    )
}

useMemo 사용하였을 때
10000할때도 멈추지 않음

  • PrimeCalculator.js
import React, { useState, useMemo } from "react";


function calculatePrimes(limit) {
    console.log(`limit: ${limit}에 대한 소수 계산`)
    const primes = [];
    for(let i=2; i<=limit; i++) {
        let isPrime = true;
        for(let j=2; j<i; j++) {
            if (i % j == 0) {
                isPrime = false;
                break;
            }
        }
        
        if (isPrime) {
            primes.push(i);
        }
    }

    return primes;
}

export default function PrimeCalculator(props) {
    const [limit, setLimit] = useState(10);
    // const primes = calculatePrimes(limit);
    const primes = useMemo(() => calculatePrimes(limit), [limit]);
    return (
        <div>
            <input 
                type="number" 
                value={limit} 
                onChange={(e) => setLimit(Number(e.target.value))}/>
            <p>계산된 소수 : {primes.join(', ')}</p>
        </div>
    )
}

useCallback(callback:Fn, deps:Array)

함수를 정의하는 것도 비용이다
함수 정의도 dependency가 변경될 때에만 재정의하기

useCallback 사용하지 않았을 때

const [count, setCount] = useState(0)

const addCount = () => {
	setCount(count+1);
}
  • 컴포넌트가 렌더링 될 때마다 함수가 새로 생성(재정의)됨.
    • 메모리 뺏다 넣고 뻇다 넣고 되니까 낭비
  • 계속 재정의하기 번거로우니 아래처럼 변경

useCallback 사용하였을 때

const addCount = useCallback(() => {
	setCount(count);
}, [count])
  • deps에 있는 count가 변할때만 함수가 새로 생성(재정의)됨.

🤖 GPT Say..

  • useCallback 훅을 사용하면 리액트 컴포넌트가 렌더링될 때마다 함수가 새로 생성되는 것을 방지할 수 있습니다.
    함수가 렌더링 될 때마다 새로 생성되는 경우, 해당 함수를 포함한 컴포넌트나 하위 컴포넌트가 리렌더링되는데, 이는 불필요한 성능 손실을 초래할 수 있습니다.

  • addCount 함수를 살펴봅시다.
    useCallback을 사용하지 않으면, 함수 내부에서 count 변수에 직접 접근하여 상태를 변경하는데, 이 때마다 addCount 함수가 렌더링 될 때마다 새로 생성됩니다.
    그리고 새로 생성된 함수는 새로운 레퍼런스를 가지고 있기 때문에, 이 함수를 참조하는 곳들은 새 함수가 생성되었다는 사실을 인지하고 다시 렌더링됩니다.

  • 하지만 useCallback을 사용하면, 의존성 배열에 명시된 값들이 변하지 않는 한, 해당 함수는 렌더링 될 때마다 새로 생성되지 않습니다. 따라서 addCount 함수가 여러 번 렌더링될 때마다 동일한 함수 레퍼런스를 반환하므로, 함수를 참조하는 곳들이 불필요하게 다시 렌더링되지 않게 됩니다.

  • 결론적으로, useCallback을 사용하면 함수를 새로 생성하는 불필요한 오버헤드를 방지하여 성능을 향상시킬 수 있습니다.

useCallback vs useMemo

🤖 GPT Say..
useCallbackuseMemo는 모두 리액트 훅이며, 둘 다 성능 최적화를 위해 사용됩니다.
그러나 각각의 주요 목적과 사용 사례에는 차이가 있습니다.

  1. useCallback:

    • 주로 콜백 함수를 메모이제이션(memoization)하는 데 사용됩니다.
    • 함수를 새로 만들지 않고 이전에 생성된 함수를 재사용할 수 있도록 합니다.
    • 의존성 배열을 통해 콜백 함수가 의존하는 값들을 명시하여, 해당 값들이 변경되지 않으면 같은 함수를 반환하도록 제어할 수 있습니다.
    • 이전에 생성된 함수를 재사용함으로써 렌더링 성능을 향상시킬 수 있습니다.
  2. useMemo:

    • 주로 값을 메모이제이션(memoization)하는 데 사용됩니다.
    • 계산 비용이 높은 연산의 결과를 캐시하여, 이전에 계산된 값이 필요할 때 재사용할 수 있도록 합니다.
    • 의존성 배열을 통해 값이 변경되지 않으면 캐시된 값을 유지하고, 변경되면 재계산합니다.
    • 주로 계산 비용이 높은 연산의 결과를 캐시하여 성능을 향상시키는 데 사용됩니다.

간단하게 말하면, useCallback은 함수를, useMemo는 값을 메모이제이션하는 데 사용됩니다.
useCallback은 콜백 함수의 생성을 최적화하는 데 사용되고, useMemo는 값을 캐싱하여 계산 비용을 줄이는 데 사용됩니다.

useContext

컴포넌트 Tree 내에서 데이터를 상호간에 공유할 때 사용하는 훅
(너무 많이 사용하는 것은 관리 비용이 증가할 수 있음)

  • ThemeProvider.js

  • MyPage.js

  • ThemeButton.js

  • App.js

import logo from './logo.svg';
import './App.css';
import { useState } from 'react';
import TodoList from './components/TodoList.js';
import { ThemeProvider } from './components/ThemeProvide.js';
import ThemeButton from './components/ThemeButton.js';
import MyPage from './components/MyPage.js';

function App() {
  const [visible, setVisible] = useState(false)
  
  return (
      <ThemeProvider>
        <ThemeButton/>
        <MyPage/>
      </ThemeProvider>
  );
}
export default App;

0개의 댓글