FrontEnd/React.js

React Hooks 알아보기

SambaLim 2020. 3. 10. 21:35

📌

React Hooks

React 16.8로 업데이트 되며 class를 작성하지 않고도 state 와 같은 React의 기능을 사용할 수 있게 되었습니다.

import React, { useState } from 'react';

function Example() {
  // "count"라는 새로운 상태 값을 정의합니다.
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

Hook의 특징

Hook의 특징은 다음과 같습니다.

  • 선택적 사용: 기존의 코드를 다시 작성할 필요 없이 컴포넌트 안에서 Hook을 사용할 수 있습니다. Hook이 필요없는 경우에는 굳이 사용하실 필요는 없습니다.
  • 100% 이전 버전과의 호환성
  • React 컨셉을 대체하지는 않습니다: Hook은 props, state, context, refs, lifecycle와 같은 React 개념에 좀 더 직관적인 API를 제공합니다.

동기

  • 컴포넌트 사이에서 상태와 관련된 로직을 재사용하기 어렵습니다.
  • 복잡한 컴포넌트들은 이해하기 어렵습니다.
  • Class는 코드의 재사용성과 코드 구성을 어렵게 만듭니다.

🥊 State Hook

아래의 예시들에서 지속적으로 보일 useState 가 바로 Hook입니다!

useState 는 함수 컴포넌트로 local state를 사용하는데 쓰입니다.

  • const [state, stateUpdaterFunction] = useState(initialStateValue)

Declare State Variable

() => {
  const [count] = useState(100)
  return <div> State variable is {count}</div>
}

Update State Variable

() => {
  const [age, setAge] = useState(19)
  const handleClick = () => setAge(age + 1)

  return (
    <div>
      Today I am {age} Years of Age
      <div>
        <button onClick={handleClick}>Get older! </button>
      </div>
    </div>
  )
}

useState 를 통해 state variable을 쉽게 변경할 수 있습니다.

Multiple State Variables

() => {
  const [age, setAge] = useState(19)
  const [siblingsNum, setSiblingsNum] = 
    useState(10)

  const handleAge = () => setAge(age + 1)
  const handleSiblingsNum = () => 
      setSiblingsNum(siblingsNum + 1)
 

  return (
    <div>
      <p>Today I am {age} Years of Age</p>
      <p>I have {siblingsNum} siblings</p>

      <div>
        <button onClick={handleAge}>
          Get older! 
        </button>
        <button onClick={handleSiblingsNum}>
            More siblings! 
        </button>
      </div>
    </div>
  )
}

위의 예시와 같이 여러개의 state도 함수형 컴포넌트로 선언하고 수정될 수 있습니다.

Use Object State Variable

() => {
  const [state, setState] = useState({ age: 19, siblingsNum: 4 })
  const handleClick = val =>
    setState({
      ...state,
      [val]: state[val] + 1
    })
  const { age, siblingsNum } = state

  return (
    <div>
      <p>Today I am {age} Years of Age</p>
      <p>I have {siblingsNum} siblings</p>

      <div>
        <button onClick={handleClick.bind(null, 'age')}>Get older!</button>
        <button onClick={handleClick.bind(null, 'siblingsNum')}>
          More siblings!
        </button>
      </div>
    </div>
  )
}

String 혹은 Number와 별개로 Object 또한 초기값으로 설정이 가능합니다.

하지만, 통합되지 않은 상태로 전체 Object를 updater function으로 전달해야 합니다.

Initialize State from Function

() => {
  const [token] = useState(() => {
    let token = window.localStorage.getItem("my-token");
    return token || "default#-token#"
  })

  return <div>Token is {token}</div>
}

Functional setState

const [value, updateValue] = useState(0)
// both forms of invoking updateValue below are valid 👇

updateValue(1);
updateValue(previousValue => previousValue + 1);

🥊 Effect Hook

Effect Hook을 사용하면, 함수 컴포넌트에서 side effect(데이터 가져오기, subscription, 수동으로 컴포넌트의 DOM수정 등)를 수행할 수 있습니다.

  • useEffect(effectFunction, arrayDependencies)
리액트의 class 생명주기 메서드에 친숙하다면, useEffect Hook을
componentDidMount, componentDidUpdate, componentWillMount가 합쳐진 것으로 생각해도 좋습니다.

Basic side effects

() => {
  const [age, setAge] = useState(0)
  const handleClick = () => setAge(age + 1)

  useEffect(() => {
    document.title = 'You are ' + age + ' years old!'
  })

  return <div>
    <p> Look at the title of the current tab in your browser </p>
    <button onClick={handleClick}>Update Title!! </button>
  </div>
}

useEffect 를 이용해서 우리는 리액트에게 컴포넌트가 렌더링 이후에 어떤 일을 수행해야 하는 지를 이야기합니다.

리액트는 우리가 넘긴 함수(effect)를 기억했다가 DOM 업데이트를 수행한 이후에 불러낼 것입니다.

useEffect 는 렌더링 이후 매번 수행됩니다. 기본적으로 첫 번째 렌더링과 이후의 모든 업데이트에서 수행됩니다.

리액트는 effect가 수행되는 시점에 이미 DOM이 업데이트 되었음을 보장합니다.

Effect with Cleanup

() => {
  useEffect(() => {
    const clicked = () => console.log('window clicked')
    window.addEventListener('click', clicked)

    // return a clean-up function
    return () => {
          window.addEventListener('click', function() {
            console.log('new');
          });
    }
  }, [])

  return <div>
    When you click the window you'll 
    find a message logged to the console
  </div>
}

Clean-up은 목적을 분명히 하기위해 Clean-up이라고 부르지만 꼭 화살표 함수를 반환할 필요는 없으며, 다른 이름으로 불러도 무방합니다.

effect에서 함수를 반환함으로 인해, 정리(Clean-up)을 위한 함수를 반환할 수 있습니다.

Skipping Effect

() => {
  const [randomNumber, setRandomNumber] = useState(0)
  const [effectLogs, setEffectLogs] = useState([])

  useEffect(
    () => {
      setEffectLogs(prevEffectLogs => [...prevEffectLogs, 'effect fn has been invoked'])
    },
    []
  )

  return (
    <div>
      <h1>{randomNumber}</h1>
      <button
        onClick={() => {
          setRandomNumber(Math.random())
        }}
      >
        Generate random number!
      </button>
      <div>
        {effectLogs.map((effect, index) => (
          <div key={index}>{'🍔'.repeat(index) + effect}</div>
        ))}
      </div>
    </div>
  )
}

useEffect 에서 특정 값들이 리렌더링 시에 변경되지 않는다면, useEffect 의 두 번째 인수로 배열을 넘겨 effect를 건너뛰도록 할 수 있습니다.

빈 배열을 넘긴다면, mount하는 경우에만 effect가 실행될 것입니다.

🖋Hook의 규칙

Hook은 Javascript 함수이지만, Hook을 사용할 때는 두 가지 규칙을 준수해야 합니다.

  • 최상위(at the Top Level)에서만 Hook을 호출해야 합니다.
    • 반복문, 조건문 혹은 중첩함수 내에서 Hook을 호출해서는 안됩니다.
  • 오직 React 함수 내에서 Hook을 호출해야 합니다.
    • 일반적인 Javascript 함수에서 호출해서는 안됩니다.

참고자료

'FrontEnd > React.js' 카테고리의 다른 글

React Hook-flow 이해하기  (0) 2021.11.26
useEffect 이해하기  (1) 2021.10.28
React Ref 이해하기  (0) 2021.09.29
React 에서 key값을 index로 하면 안되는 이유  (2) 2021.07.29
상태 관리에 대해 정리해보자  (0) 2020.12.30