훅(Hook) 과 useState

</>·2022년 4월 24일
0
post-thumbnail

목표

  • 훅(Hook)에 대해 알아보기

1. 훅(Hook)

  • 리액트는 훅에 대해 다음과 같이 설명하였다.

Hook은 React 버전 16.8부터 React 요소로 새로 추가되었습니다. Hook을 이용하여 기존 Class 바탕의 코드를 작성할 필요 없이 상태 값과 여러 React의 기능을 사용할 수 있습니다.


1-1. 훅은 왜 나타나게 된걸까?

  • 리액트에서 훅이 나타나게 된 동기를 다음과 같이 설명하였다.

    Hook은 우리가 5년간 React로 컴포넌트를 작성하고 유지하는 동안 부딪혔던 수 많은 문제들을 해결했습니다. React를 배우는 중이든, 매일 사용하든, 심지어 비슷한 컴포넌트 모델과 함께 다른 라이브러리를 선호하든지 간에, 사용자는 이러한 문제를 인식해 왔을 것 입니다.


  • 위와 같은 설명을 하면서 클래스의 단점들을 나열하였다.
    1. 컴포넌트 사이에서 상태 로직을 재사용하기 어렵다.
      • 리액트는 컴포넌트간에 재사용 가능한 로직을 붙이는 방법을 제공하지 않는다.
    2. 복잡한 컴포넌트들은 이해하기 어렵다.
      • componentDidMount, componentDidUpdate 등과 같은 상태 관련 로직은 한 공간안에 묶여 있기 때문에 이런 컴포넌트들을 작게 분리하는 것은 불가능하며 테스트하기도 어렵다.
    3. 클래스는 사람과 기계를 혼동시킨다.
      • 리액트 내의 함수와 Class 컴포넌트의 구별, 각 요소의 사용 타이밍 등은 숙련된 React 개발자 사이에서도 의견이 일치하지 않는다.

  • 클래스의 단점들을 설명하면서 대비되는 훅의 장점은 다음과 같다.
    1. 훅을 사용하면 컴포넌트로부터 상태 관련 로직을 추상화할 수 있어 독립적인 테스트재사용이 가능하다.
    2. 훅은 계층의 변화 없이 상태 관련 로직을 재사용할 수 있도록 해준다.
    3. 훅을 통해 서로 비슷한 것을 하는 작은 함수의 묶음으로 컴포넌트를 나누는 방법을 사용할 수 있다.

결국 훅(Hook)은 클래스의 단점을 보완하면서 라이프사이클 등과 관련된 함수를 재사용 가능하도록 해준다.


1-2. 훅의 사용 규칙

  • 훅은 단순히 자바스크립트 함수지만 두 가지 규칙을 준수해야 한다.
    1. 최상위(top level)에서만 훅을 호출해야 한다. 반복문, 조건문, 중첩 함수 내에서 훅을 실행하면 안된다. 원하지 않은 사이드 이펙트가 발생할 수 있다.
    2. 리액트 함수 컴포넌트 내에서만 훅을 호출해야 한다. 커스텀 훅(custom hook)을 제외하고 일반 자바스크립트 함수 내에서는 훅을 호출해서는 안된다.
  • 이 규칙들을 강제하기 위해 linter plugin을 제공한다.



2. useState


2-1. 클래스의 상태 변화

class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>
          Click me
        </button>
      </div>
    );
  }
}
  • 클래스에서는 state 변수인 count 값을 변경하려면 this.state.count를 사용해서 변경해야 했다.

2-2. 훅의 상태 변화

  • useState는 state 변수를 선언해주는 역할을 한다.이는 클래스 컴포넌트의 this.state가 제공하는 기능과 똑같다.
  • 일반적으로 일반 변수는 함수가 끝날 때 사라지지만, state 변수는 리액트에 의해 사라지지 않는다.
import React, { useState } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}
  • 첫번째 줄은 useState라는 훅을 리액트에서 가져오는 것을 의미한다.
  • 네번째 줄은 useState 훅을 이용해 state 변수와 해당 state를 갱신할 수 있는 함수를 만들고 0으로 초기화 했음을 의미한다.
  • 아홉번째 줄은 setCount 함수를 호출하여 state 변수를 갱신할 수 있음을 의미한다.
  • 이 때, state 변수가 갱신하게 되면 리액트는 새로운 count 변수를 Example 컴포넌트에 넘기며 해당 컴포넌트를 리렌더링한다.

const [count, setCount] = useState(0);
  • 자바스크립트 문법을 잘 모른다면 위 코드가 헷갈릴 수 있다.
  • 이는 배열 구조 분해 라는 문법인데 count와 setCount 총 2개의 값을 만드는 역할을 한다.
  const countStateVariable = useState(0); // 두 개의 아이템이 있는 쌍을 반환
  const count = fruitStateVariable[0]; // 첫 번째 아이템
  const setCount = fruitStateVariable[1]; // 두 번째 아이템
  • 배열 구조를 풀어서 해석해보면 useState를 이용하여 변수를 선언하면 2개의 아이템 쌍이 들어있는 배열로 만들어진다.
  • 위 코드보다는 배열 구조 분해를 사용하는 것이 가독성 측면에서 좋다.

2-3. 초기값

const [count, setCount] = useState(0);
  • useState의 인자로 위 코드에서는 0을 넘겨 주었는데 이는 state 변수의 초기값을 설정해주는 것이다.
  • localStorage와 같이 읽고 쓰는(I/O) 작업은 언제든지 딜레이가 발생할 수 있기 때문에 초기값을 불러오지 못하는 문제가 발생할 수 있다.
const [count, setCount] = useState(() => {
  return window.localStorage.getItem("count");
});
  • useState에 값을 인자로 넘겨주지 않고 화살표 함수를 인자로 넘겨 주게 되면 초기값을 읽어 오는데 시간이 좀 걸리더라도 함수가 결과 값을 반환하면 초기 값을 불러올 수 있다.
  • 이렇게 변수 대신 함수를 넘겨 초기화 작업을 지연시킬 수 있는데, 이를 지연 초기화(lazy initialization)라고 한다.

2-4. state 가져오기

<p>Your visit is {this.state.count} times.</p>
  • 클래스 컴포넌트는 this.state를 사용해서 값을 가져온다.
<p>Your visit is {count} times.</p>
  • 하지만, 함수 컴포넌트(훅)는 count를 직접 사용해 값을 가져온다.

2-5. state 갱신하기

<button onClick={() => this.setState({count: this.state.count + 1})}>
  클릭
</button>
  • 클래스 컴포넌트는 count를 갱신하기 위해 this.setState()를 호출한다.
<button onClick={() => setCount(count + 1)}>
  클릭
</button>
  • 하지만, 함수 컴포넌트(훅)는 setCount와 count 변수를 가지고 있기에 this를 호출하지 않아도 된다.

출처

profile
개발자가 되고 싶은 개발자

0개의 댓글