[React] Hooks

에구마·2023년 12월 7일
0

React

목록 보기
1/5

useEffect

useEffect(setup, dependencies?)

useEffect는 컴포넌트를 외부 시스템과 동기화할 수 있는 React 훅입니다.

_외부 시스템이란 리액트에 의해 제어되지 않는 (API 통신, ref를 통한 dom 제어, window 전역의 이벤트 리스너)

Usage

  1. 특정 값에 대한 변화 감지

    useEffect(() => {
        console.log("clicked");
      }, [count]); // count 변화 감지
  2. 라이프사이클처럼 사용할 수도 있음

    useEffect(() => {
      console.log("component loaded");
    }, []); //컴포넌트 로드 감지 (라이프사이클))

    deps에 빈배열만 둘 경우, 컴포넌트가 처음 로드될 때 발생함.

    —> 컴포넌트가 마운트된 후, 컴포넌트가 업데이트 되고 난 후, 컴포넌트가 언마운트 되기 전 모두 실행

  3. 전역적으로 이벤트 감지

    useEffect(() => {
      console.log("event");
      const handleScroll = () => {
        console.log(window.scrollY);
      };
      document.addEventListener("scroll", handleScroll);
    	return () => document.removeEventListener("scroll", handleScroll);	
    	// 전역적으로 이벤트를 잡았을 땐 꼭 해제해줘야 한다!!
    	// --> 여기서 useEffect의 return은 **해당 컴포넌트가 제거될때 실행**이 된다! 
    
    }, []);

useRef

useRef는 렌더링에 필요하지 않은 값을 참조할 수 있는 React 훅입니다.

import { useRef } from 'react';

function MyComponent() {
	const **inputRef** = useRef(null);
	return <input ref={**inputRef**} / > // 이렇게 JSX노드의 ref 속성으로 전달되면 **.current**로 접근!!
}

Usage

  1. DOM에 직접 접근

    function App() {
      const inputRef = useRef();
    
      return (
        <div>
          <input ref={inputRef} />
          <button onClick={() => inputRef.current.focus()}>Focus</button>
        </div>
      );
    }

    +) 컴포넌트를 통해서도 DOM에 접근 가능

    import "./App.css";
    import { useRef } from "react";
    import Input from "./components/Input";
    
    function App() {
      const inputRef = useRef();
    
      return (
        <div>
          <Input **ref={inputRef**} />
          <button onClick={() => **inputRef**.**current**.focus()}>Focus</button>
        </div>
      );
    }
    
    export default App;

    부모 컴포넌트인 App에서 하위 컴포넌트인 Input의 DOM요소에 접근하려할때 사용할 수 있다는것

    import React from "react";
    const Input = React.**forwardRef**((_, ref) => {
      return (
        <>
          Input : <input **ref={ref}** />
        </>
      );
    });
    
    export default Input;

    인자로 받은 ref를 DOM요소에 등록해놓으면 부모에서의 ref와 연결되어 접근 가능

    ** const Input = ({ ref }) => { 등, ref를 props로 넘길수 있지만, forwardRef 사용을 권장한다. ref를 숨길 수 있고 표준이기 때문이다. **

    forwardRef 문서

    import { forwardRef } from 'react';
    
    const MyInput = forwardRef(function MyInput(props, ref) {
      // ...
    });
    
    // forwardRef( render함수 ) 형식임
    // render 함수는 (props, ref) => { } 혹은 function FuncName(props,ref) { }
  2. (다시 렌더링하지 않을!!) 지역 변수로 사용할 때

    useRef는 값이 변경되더라도 다시 렌더링하지 않는다!! ↔useState는 값이 변경될 때 다시 렌더링 한다.

    import "./App.css";
    import { useRef } from "react";
    import Input from "./components/Input";
    import AutoCounter from "./components/AutoCounter";
    
    function App() {
      const inputRef = useRef();
    
      return (
        <div>
          <Input ref={inputRef} />
          <button onClick={() => inputRef.current.focus()}>Focus</button>
          **<AutoCounter />**
        </div>
      );
    }
    
    export default App;
    import { useRef, useState } from "react";
    
    const AutoCounter = () => {
      const [count, setCount] = useState(0); //state값을 증가시키지만 
      **const intervalId = useRef();**
    
      const handleStart = () => {
        intervalId.current = setInterval(() => { //intervalId값 변경이기 때문에 재렌더링XX
          setCount((count) => count + 1);
        }, 1000);
      };
      const handleStop = () => {
        clearInterval(intervalId.current);
      };
      return (
        <>
          <div>{count}</div>
          <button onClick={handleStart}>Start</button>
          <button onClick={handleStop}>Stop</button>
        </>
      );
    };
    
    export default AutoCounter;

    start를 눌러 1초마다 값이 증가하는게 화면에 그려지지만 재렌더링이 일어나진 않는다.

    useRef를 통해 값을 넣어줬으니까.

useMemo

useMemo는 리렌더링 사이의 계산 결과를 캐시할 수 있는 React 훅입니다.

연산”결과”를 캐싱하는 느낌

const cachedValue = useMemo(calculateValue, dependencies)

// sum() 이 매우 복잡하고 큰 연산이고, n이 상태라면
// const cachedResult = sum(n) 보다

const cachedResult = useMemo( () => sum(n), [n]); 

컴포넌트는 1)자신의상태변경, 2)부모로부터의prop변경, 3)부모의상태변경 시 재렌더링된다. 이 모든 경우에서 내부 함수나 연산을 재렌더링하지 않고 dependencies의 변경시에만 재렌더링한다.

*** React.memo와의 차이

단어가 비슷해서 헷갈리는데.. useMemo()는 특정 값을 캐싱하는 hook 이고, React.memo는 컴포넌트를 memoization하는 HOC이다!! 자식 컴포넌트는 변경될게 없는 데 부모 컴포넌트의 변경때문에 자식까지 재렌더링 되는 것으 막을 때 사용한다.

import React from 'react';
**const Child = React.memo(** () => { ... }**)**

useCallback

useCallback은 리렌더링 사이에 함수 정의를 캐시할 수 있게 해주는 React 훅입니다.

함수 “정의”를 캐싱하는 느낌

const cachedFn = useCallback(fn, dependencies)

렌더링 시 마지막 렌더링 이후 dependencies가 변경되지 않았다면 동일한 함수 다시 제공!

import { useState, useCallback } from "react";

const useToggle = (initialState = false) => {
  const [state, setState] = useState(initialState);
  const toggle = useCallback(() => setState((state) => !state), []);

  return [state, toggle];
};
export default useToggle;

custom Hook

컴포넌트에서 박복되는 로직을 커스텀훅으로 만들 수 있다.

  • use로 시작하는 camelCase 명명 규칙을 따른다.
import { useState, useEffect } from 'react';

export default function SaveButton() {
  const [isOnline, setIsOnline] = useState(true);
  useEffect(() => {
    function handleOnline() {
      setIsOnline(true);
    }
    function handleOffline() {
      setIsOnline(false);
    }
    window.addEventListener('online', handleOnline);
    window.addEventListener('offline', handleOffline);
    return () => {
      window.removeEventListener('online', handleOnline);
      window.removeEventListener('offline', handleOffline);
    };
  }, []);

  function handleSaveClick() {
    console.log('✅ Progress saved');
  }

  return (
    <button disabled={!isOnline} onClick={handleSaveClick}>
      {isOnline ? 'Save progress' : 'Reconnecting...'}
    </button>
  );
}

위와 같은 네트워크 상태 확인 로직을 useOnlineStatus라는 훅으로 추출하면,

import { useState, useEffect } from 'react';

export function useOnlineStatus() {
  const [isOnline, setIsOnline] = useState(true);
  useEffect(() => {
    function handleOnline() {
      setIsOnline(true);
    }
    function handleOffline() {
      setIsOnline(false);
    }
    window.addEventListener('online', handleOnline);
    window.addEventListener('offline', handleOffline);
    return () => {
      window.removeEventListener('online', handleOnline);
      window.removeEventListener('offline', handleOffline);
    };
  }, []);
  return isOnline;
}

**/// App.js에서 호출은 간결하게 되고 재사용 또한 간단해진다!**
import { useOnlineStatus } from './useOnlineStatus.js';

function StatusBar() {
  const isOnline = useOnlineStatus();
  return <h1>{isOnline ? '✅ Online' : '❌ Disconnected'}</h1>;
}

function SaveButton() {
  const isOnline = useOnlineStatus();

  function handleSaveClick() {
    console.log('✅ Progress saved');
  }

  return (
    <button disabled={!isOnline} onClick={handleSaveClick}>
      {isOnline ? 'Save progress' : 'Reconnecting...'}
    </button>
  );
}

export default function App() {
  return (
    <>
      <SaveButton />
      <StatusBar />
    </>
  );
}

배우며추가예정





✔️ react.dev 공식문서와 강의를 학습하면서 정리한 내용을 담았습니다! 잘못된 부분 알려주시면 감사하겠습니다 :)

profile
코딩하는 고구마 🍠 Life begins at the end of your comfort zone

0개의 댓글