Skip to content

Latest commit

 

History

History
302 lines (191 loc) · 9.63 KB

220708.md

File metadata and controls

302 lines (191 loc) · 9.63 KB

리액트 훅(React Hooks) 소개

리액트 훅이란?

  • 함수형 컴포넌트에서도 클래스형 컴포넌트의 기능을 사용할 수 있게 하는 기능이다.
  • 기존에 함수형 컴포넌트에서 못하던 상태값 관리, 컴포넌트의 생명 주기 함수를 이용할 수 있다.

리액트 훅이 필요한 이유

  • 함수형 컴포넌트들은 기본적으로 리렌더링이 될 때, 함수 안에 작성된 모든 코드가 다시 실행되어 기존에 가지고 있던 상태(state)를 전혀 기억할 수 없다.
  • 리액트 훅을 통해 브라우저에 메모리를 할당함으로써 함수형 컴포넌트가 상태(state)를 가질 수 있게 되었다.
  • => 클래스형 컴포넌트에서만 가능하던 상태관리를 더 손쉽게 할 수 있어 필요하다.

Hook의 규칙

  1. 최상위에서만 Hook을 호출
    • React 함수의 최상위에서만 Hook을 호출할 것
    • 반복문, 조건문, 중첩된 함수 등에서 호출 X
  2. React 함수에서만 Hook을 호출
    • Custom Hook에서는 호출 가능
    • 일반적인 Javascript 함수에서는 호출 X
  3. Hook을 만들 때 앞에 use 붙이기
  4. React는 Hook이 호출되는 순서에 의존
    • 한 컴포넌트에서 여러개의 Hook이 사용되는 경우 Hook은 위에서부터 아래로 순서에 맞게 동작한다.

리액트 훅 종류

기본 Hooks

  • useState (동적 상태 관리)
  • useEffect (side effect 수행 -mount/unmount/update)
  • useContext (컴포넌트를 중첩하지 않고도 전역 값 쉽게 관리)

추가 Hooks

  • useReducer (복잡한 컴포넌트들의 state를 관리/분리)
  • useCallback (특정 함수 재사용)
  • useMemo (연산한 값 재사용)
  • useRef (DOM선택, 컴포넌트 안에서 조회/수정할 수 있는 변수 관리)
  • useImperativeHandle
  • useLayoutEffect
  • useDebugValue

+ 사용자 Custom Hook


useEffect

useEffect를 사용하여 마운트/언마운트/업데이트시 할 작업 정하기

마운트 / 언마운트

  • useEffect를 사용할 때 첫번째 파라미터에는 함수, 두번째 파라미터에는 의존값이 들어있는 배열(deps)을 넣는다.

  • deps배열을 비우게 된다면, 컴포넌트가 처음 나타날 때에만 useEffect에 등록한 함수가 호출된다.

  • useEffect는 함수를 반환할 수 있고, 이를 cleanup 함수라고 부른다.

  • deps 배열이 비워져 있다면 컴포넌트가 사라질 때 cleanup 함수가 호출된다.

  • useEffect(() => {
        console.log('컴포넌트가 화면에 나타남');
        return () => {
            console.log('컴포넌트가 화면에서 사라짐');
        };
    }, []);
    
  • 마운트 시 주로 하는 작업

    • props로 받은 값을 컴포넌트의 로컬 상태로 설정
    • 외부 API 요청 (REST API 등)
    • 라이브러리 사용 (D3, video.js 등)
    • setInterval을 통한 반복 작업 혹은 setTimeout을 통한 작업 예약
  • 언마운트 시 주로 하는 작업

    • setInterval, setTimeout을 사용하여 등록한 작업들 clear하기 (clearInterval, clearTimeout)
    • 라이브러리 인스턴스 제거

deps에 특정 값 넣기

  • deps에 특정 값을 넣으면 컴포넌트가 처음 마운트될 때도 호출이 되고, 지정한 값이 바뀔 때에도 호출이 된다.

  • cleanup 함수를 통해 언마운트시에도 호출이 되고, 값이 바뀌기 직전에도 호출이 된다.

  • useEffect(() => {
        console.log('user 값이 설정됨');
        console.log(user);
        return () => {
            console.log('user 가 바뀌기 전..');
            console.log(user);
        };
    }, [user]);
    
  • 주의! useEffect 안에서 사용하는 상태나 props가 있다면 useEffect의 deps에 넣어야 한다.

deps 파라미터 생략하기

  • deps 파라미터를 생략한다면, 컴포넌트가 리렌더링될 때마다 호출된다.

clean up 함수 예시

useEffect(() => {
    const identifier = setTimeout(() => {
        setFormIsValid(enteredEmail.includes('@') && enteredPassword.trim().length > 6);
    }, 500);

    return () => {
        clearTimeout(identifier);
    };
}, [enteredEmail, enteredPassword]);
  • 키를 타이핑할 때마다 state를 업데이트 하는 것이 아니라 일정량 이상부터 키 입력을 수집하거나 키 입력 후 일정시간 동안 일시 중지되는 것을 기다린 후에 확인하고 싶을 수 있다.
  • 모든 키 입력에 대해 500밀리초 후에 작업이 일어나는 것이 아니라 타이머를 저장하고 다음에 키가 입력되면 새로운 타이머를 설정하기 전에 전 타이머를 clean up 함수를 통해 지운다.

React.memo()

React.memo()로 불필요한 재평가 방지하기

  • 리액트에서는 부모컴포넌트가 재평가되면 자식컴포넌트가 변경되지 않아도 재평가되는 문제점이 있다.

  • React.memo() 함수는 자식 컴포넌트의 props가 바뀌지 않았다면, 리렌더링을 방지하여 컴포넌트의 리렌더링 성능 최적화를 해준다.

  • 사용방법

    • import React from 'react';
      
      const CreateUser = ({ username, email, onChange, onCreate }) => {
        return (
          <div>
      		...
          </div>
        );
      };
      
      export default React.memo(CreateUser);
      
    • 함수형 컴포넌트에서 props가 바뀌었는지 확인할 컴포넌트를 지정한 후에 React.memo()로 감싼다.

  • React.memo는 인자로 들어간 컴포넌트에 어떤 props가 입력되는지 확인하고 입력되는 모든 props의 신규값을 확인한 뒤 기존의 props의 값과 비교하도록 리액트에 전달하고, props의 값이 바뀐 경우에만 컴포넌트를 재실행 및 재평가한다.

  • 하지만 최적화에도 비용이 따르기 때문에 적절하게 사용하는 것이 중요!

문제점

  • App.js

    import React, { useState, useCallback } from 'react';
    
    import Button from './components/UI/Button/Button';
    import DemoOutput from './components/Demo/DemoOutput';
    import './App.css';
    
    function App() {
      const [showParagraph, setShowParagraph] = useState(false);
    
      console.log('APP RUNNING');
    
      const toggleParagraphHandler () => {
        setShowParagraph((prevShowParagraph) => !prevShowParagraph);
      }
     
      return (
        <div className="app">
          <h1>Hi there!</h1>
          <DemoOutput show={false} />
          <Button onClick={toggleParagraphHandler}>Toggle Paragraph!</Button>
        </div>
      );
    }
    
    export default App;
    
  • DemoOutput.js

    import React from 'react';
    
    import MyParagraph from './MyParagraph';
    
    const DemoOutput = (props) => {
      console.log('DemoOutput RUNNING');
      return <MyParagraph>{props.show ? 'This is new!' : ''}</MyParagraph>;
    };
    
    export default React.memo(DemoOutput);
    
    
  • Button.js

    import React from 'react';
    
    import classes from './Button.module.css';
    
    const Button = (props) => {
      console.log('Button RUNNING');
      return (
        <button
          type={props.type || 'button'}
          className={`${classes.button} ${props.className}`}
          onClick={props.onClick}
          disabled={props.disabled}
        >
          {props.children}
        </button>
      );
    };
    
    export default React.memo(Button);
    
  • DemoOutput과 Button 컴포넌트 모두 React.memo를 적용했는데 Button 컴포넌트가 props가 바껴도 계속 재렌더링이 되는 문제점이 생겼다.

    • 이유: 버튼에 전달되는 함수가 매번 재생성되기 때문

useCallback

useCallback을 사용하여 함수 재사용하기

  • 컴포넌트 실행 전반에 걸쳐 함수를 저장할 수 있게 하는 훅이다.

  • 리액트에 이 함수를 저장하여 매번 실행할 때마다 이 함수를 재생성할 필요가 없다는 것을 알릴 수 있다.

  • 선택한 함수를 리액트의 내부 저장 공간에 저장해서 함수 객체가 실행될 때마다 이것을 재사용한다.

  • 저장하려는 함수를 래핑하여 사용한다.

  • const toggleParagraphHandler = useCallback(() => {
        setShowParagraph((prevShowParagraph) => !prevShowParagraph);
    }, []);
    
  • useCallback 훅을 사용하고 함수를 첫 번째 인자로 전달하면 useCallback은 저장된 함수를 반환한다.

  • 이 App 함수가 다시 실행되면 useCallback이 리액트가 저장된 함수를 찾아서 같은 함수 객체를 재사용하게 된다.

  • 어떤 함수가 절대 변경되어서는 안된다면 useCallback을 사용해 함수를 저장하면 된다.

  • 두번째 인자는 useCallback 호출에 대한 의존성 배열로 함수 안에서 사용하는 상태 혹은 props가 있다면 이 의존성 배열 안에 포함시켜야 한다.

useMemo

useMemo를 사용하여 연산한 값 재사용하기

  • 연산된 값을 useMemo를 이용하여 재사용하여 성능을 최적화한다.

  • useCallback이 함수에 대한 것을 저장하듯이 모든 종류의 데이터를 저장할 수 있다.

  • 첫 번째 인자에는 저장하고 싶은 것을 반환할 함수가 들어간다.

  • 두 번째 인자에는 의존성 배열이 들어가는데, 배열 안에 넣은 내용이 바뀌면 앞에 등록한 함수를 호출해서 값을 연산해주고, 내용이 바뀌지 않았다면 이전에 연산한 값을 재사용한다.

  • const { items } = props
    
    const sortedList = useMemo(() => {
        return items.sort((a, b) => a - b)
    }, [items])
    
  • const listItems = useMemo(() => [5, 3, 1, 10, 9], [])
    
    return(
      <DemoList title={listTitle} items={listItems} />
    )