React | Hooks

Jihun Kim·2021년 8월 5일
0

리액트

목록 보기
1/3
post-thumbnail

Hook이란?

  • Hook은 기존 함수형 컴포넌트에서도 클래스형 컴포넌트 기능을 사용할 수 있게 하는 기능을 말한다.
  • hook은 컴포넌트에서 데이터를 관리(state)하고 데이터가 변경될 때 상호작용(effect) 하기 위해 사용한다.

👉 함수형 컴포넌트에서 React State와 생명주기 기능을 연동할 수 있도록 만들어 줌

리액트 16.8 하위 버전까지는 state를 사용하려면 클래스 컴포넌트(복잡해 오류가 나기 쉬우며 유지보수가 어렵다.)를 사용해야 했다. 이제는 함수형 컴포넌트에서도 state를 사용할 수 있게 되었다.

🍓 Hook의 장점

  1. UI에서 로직을 더 쉽게 분리하여 재사용성과 가독성이 높다.
  2. Hook은 컴포넌트 간 계층 변화 없이 상태 관련 로직을 재사용할 수 있도록 돕는다.

👉 Hook의 종류 2가지

  • State hook
  • Effect hook

🙈 Hook 사용시 유의사항

  • hook의 이름은 반드시 'use'로 시작해야 한다.

🧑‍💻 State hook 알아보기

클래스형 컴포넌트에서 사용하던 state를 함수형 컴포넌트에서는 'useState'를 이용해 사용할 수 있다.

사용방법

import React ,{ useState } from 'react';
const [<상태  저장 변수>, <상태  갱신 함수>] = useState(<상태 초깃값>);
  • useState를 호출하면 어레이가 반환되며 길이는 2가 된다.
  • state를 변경하기 위해서는 'setState'를 이용한다.
  • state가 변경되면 자동으로 컴포넌트가 재 렌더링 된다.

사용예시

const Example = () => {
  const [title, setTitle] = useState("");

  // state를 변경할 값 직접 입력
  setTitle("Hello");

  // 현재 값을 매개변수로 받는 함수 선언
  // return 값이 state에 반영된다.
  setTitle((cur) => {
    return cur + "world";
  });
};

🧑‍💻 Effect hook 알아보기

  • 함수형 컴포넌트에서 side effects들을 실행하기 위해 'useEffect'를 사용할 수 있다.
  • useEffect를 사용하면 모든 렌더링하는 요소마다 원하는 작업을 수행할 수 있어 코드의 중복이 줄어든다.
  • 클래스의 생명주기 메소드(componentDidMount 등)를 구현할 필요가 없다.

👀 side effects란?

리액트 컴포넌트 안에서 데이터를 가져오거나 구독하고, DOM을 직접 조작하는 모든 동작을 말한다.

👉 즉, 함수 외부에서 로컬의 상태 값을 변경하는 것을 말한다(이는 다른 컴포넌트에 영향을 줄 수도 있어 렌더링 과정에서는 구현할 수가 없다.).

사용방법

const Example = () => {
  useEffect(EffectCallback, Deps)
}
  • Effect hook을 사용하면 어떠한 값이 변할 때마다 내가 설정한 함수가 실행된다.
  • 컴포넌트 최초 렌더링시, 지정한 state나 props가 변경될 때마다 'EffectCallback' 함수가 호출된다.
    👉 여기서 '지정한 state나 props'는 'Deps'로 넘겨주게 된다.
    👉 'Deps'는 변경을 감지할 변수들의 집합(배열)이다.

사용예시

[예시1]

import { useEffect, useState } from "react";

const Counter = () => {
  const [count, setCount] = useState(0);

  // 'Deps'에 [count]를 넘겨줬다는 것은
  // count state의 변경이 일어날 때마다 콜백함수를 실행하라는 뜻
  useEffect(() => {
    console.log(`${count}번 클릭했다!`);
  }, [count]);
  return (
    <div>
      <button onClick={()=>{setCount(count+1)}}>클릭하시오</button>
    </div>
  )
};

[예시2]

  • Deps 부분에 아무것도 넘겨주지 않았는데(빈 배열), 컴포넌트 생성시 최초로 한 번은 실행되기 때문에 이는 컴포넌트 생성 또는 소멸시 한 번만 실행해 달라는 뜻이다.
    👉 Effect Callback 함수에서 반환하는 함수는 현재 상태가 종료될 때 호출된다.(반환하지 않고 실행만 하는 것은 현재 상태가 생성될 때이다.)

  • 컴포넌트가 사라지기 전에 setInterval을 멈출 필요가 있는데, 이 때 사용하는 것이 'clearInterval'이다. 즉, Deps가 바뀌어 컴포넌트를 재렌더링 하기 전에 이 함수를 실행하면 되는 것이다.
    👉 왜냐하면, 해당 컴포넌트를 사용하지 않는 상태에서도 setInterval은 계속 작동하게 되는데 계속 렌더링 해서 부하를 줄 필요가 없기 때문이다.

import { useEffect, useState } from "react";

const Example = () => {
  useEffect(() => {
    // state가 변경될 때 혹은 컴포넌트를 재렌더링할 때 실행되는 함수
    // 생성시 실행되는 함수
    const intervalId = setInterval(() => {
      console.log("안녕하세요!");
    }, 1000);
    // 컴포넌트를 재렌더링 하기 전에, 컴포넌트가 없어질 때
    // 소멸시 실행되는 함수
    return () => {
      clearInterval(intervalId);
    };
  }, []);
};

👉 위의 예시 외에도 백엔드의 데이터(API 불러오기)를 사용해야 할 경우 'useEffect'를 사용할 수 있다.

🧩 Hook 사용 규칙

  1. React 함수 내의 '최상위'에서만 hook을 호출해야 한다.
  • 반복문, 조건문 혹은 중첩된 함수에서 hook을 호출하면 안된다.
    👉 왜냐하면, hook은 렌더링시 항상 동일한 순서로 호출되어야 하며, 그래야 useState, useEffect가 여러번 호출되어도 리액트가 hook의 상태를 올바르게 유지할 수 있기 때문이다.
  1. React 함수 내에서 hook을 호출해야 한다. 아래 두 가지 경우에서 hook을 호출할 수 있다.👇👇
  • React 함수 컴포넌트
  • 나만의 hook

만약 조건부로 side effect를 실행하려면 useEffect를 조건문 안에 넣어서는 안되며 조건문을 useEffect 안에 넣어야 한다.
리액트는 hook이 호출되는 순서에 의존하기 때문에 반드시 함수의 최상위에서 hook을 사용해야 한다.

🐥 다른 이외의 Hooks 알아보기

다른 여러가지의 hooks가 있지만 아래의 세 개만 알아본다.👇👇

useMemo, useCallback, useRef

  1. useMemo
  • 지정한 state나 props가 변경될 경우 해당 값을 활용해 계산된 값을 memoization한다.
    👉 이를 통해 재렌더링시 불필요한 연산을 줄일 수 있다.
  • 단, 해당 연산은 렌더링 단계에서 이루어지므로 복잡하거나 시간이 오래 걸리는 로직을 사용하지는 않아야 한다!

사용 예시

const Example = () => {
  const [firstName, setFirstName] = useState('영희')
  const [lastName, setLastName] = useState("김");

  // 이름과 성이 바뀔 때마다 풀네임을 메모이제이션 한다.
  const fullName = useMemo(()=>{
    return `${firstName} ${lastName}`
  }, [firstName, lastName])

}
  1. useCallback
  • useMemo가 '변수'를 메모한다면, useCallback은 '함수'를 메모한다.
  • useMemo와 useCallback의 원리는 동일하다. 단지 개발자의 편의를 위해서 리액트에서 나눈 것 뿐임!
const Example = () => {
  const [firstName, setFirstName] = useState('영희')
  const [lastName, setLastName] = useState("김");

  // 이름과 성이 바뀔 때마다 풀네임을 메모이제이션 한다.
  const fullName = useCallback(()=>{
    return `${firstName} ${lastName}`
  }, [firstName, lastName])
  
  return (
  	<div>
      // 함수를 호출해 리턴 값을 반환한다.
      {fullName()}
    </div>
  );
}
  1. useRef
  • 컴포넌트 생명주기 내에서 유지할 ref 객체를 반환한다.
  • ref 객체는 '.current'라는 프로퍼티를 갖는데 이 값을 자유롭게 변경할 수 있다.
    👉 이는 'e.target'과 같은 값을 반환한다.
  • useRef에 의해 반환된 ref 객체가 변경되어도 컴포넌트는 재렌더링 되지 않는다!
    👉 개발을 하다보면 변경이 되더라도 다시 랜더링 되지 않아야 할 상황이 생기는데, 이 때 사용할 수 있다.
  • 일반적으로 DOM 요소에 접근할 때 사용한다.
const Example = () => {
  const inputRef = useRef(null);
  const onButtonClick = () => {
    // inputRef.current에는 input element object가 할당됨
    // focus를 사용하면 input element에 focus하라는 뜻
    inputRef.current.focus();
    // 아래와 같이 value를 사용하면 input element의 value값을 가져올 수 있음
    inputRef.current.value();
  };
  return (
    <div>
      <input ref={inputRef} type="text" />
      <button onClick={onButtonClick}>
        input으로 포커스
      </button>
    </div>
  )
};

🔥나만의 hook 만들기

🤷‍♀️ 나만의 hook은 언제 사용하면 좋을까?

  • 입력된 이름에 따라 관리자인지 확인하는 상태(예시: const [isAdmin, setBeAdmin] = useState(false)) 같은 것이 추가될 수 있기 때문에 나만의 Hook을 만들어 State Hook과 분리하는 좋다.
  • 스케일이 큰 프로젝트를 할 때 state 관리하기에 좋다.

나만의 hook을 통해 로직의 재사용성을 높일 수 있다.

나만의 hook 사용규칙

  • hook의 이름은 'use'로 시작해야 한다.
  • 한 hook 내의 state는 공유되지 않는다.

나만의 hook 예시

const useUser = () => {
  const [nickname, setNickname] = useState('')

  const updateNickname = event => {
    const nickname = event.target.value

    setNickname(nickname)
  }

  // updateNickname 메소드를 함께 반환한다.
  return [nickname, updateNickname]
}

// 아래와 같이 User 컴포넌트에서 useUser() 를 호출해서 사용할 수 있다. 
// 그러면 아래의 'setNickname'은 'updateNickname'함수가 된다.
const User = () = {
  const [nickname, setNickname] = useUser();
  ...
}

[Toggle 사용 예시]

<./hooks/useToggle.js>

  • 반환 값을 적을 때 대괄호 대신 중괄호 '{ }'를 사용해도 된다.
const useToggle = (initialValue) => {
    const [isOn, setIsOn] = useState(initialValue);
    const toggle = ()=> {
        setIsOn((cur) => {
            return !cur;
        });
    }
    
    return [isOn, toggle];
}

<App.js>

  • useToggle의 리턴 값 [isOn, toggle]을 받았다.
  • 여기서는 이미 useToggle에 toggle을 정의해 두었기 때문에 'setToggle'이라고 사용하지 않아도 된다.
  • useToggle()의 initialValue는 아래에서 'false'가 된다.
function App() {
    const [isOn, toggle] = useToggle(false);
   	...

🧗 부록!

왜 컴포넌트 내에서 변수를 안쓰고 state를 사용할까?

  • 함수가 실행될 때마다 내부 변수가 초기화 된다. 즉, 컴포넌트가 렌더링 될 때마다 변수가 초기화 되어 사용 불가!
    👉 하지만, state는 렌더링 되어도 값을 유지한다.

왜 컴포넌트 내에서 변수를 안쓰고 state를 사용할까?

  • 부모 컴포넌트가 렌더링 됐을 때
  • props가 변경 되었을 때
  • state가 변경 되었을 때

🐧 이 글은 엘리스 AI 트랙 과정 중 'React 기초' 강의 내용을 바탕으로 작성 되었습니다.

profile
쿄쿄

0개의 댓글