Pandaman Blog

[React.js]React Hook 본문

Front end/React

[React.js]React Hook

oyg0420 2020. 6. 1. 23:09

React Hook은 상태 로직의 재사용성을 높이고, class 형태의 React Component의 복잡도를 낮추기 위해 만들어졌습니다. Hook을 사용하면 class형 컴포넌트에서 사용하던 lifeCycle에 대해 달달 외우지 않아도 됩니다. 코드의 로직을 이해하면서 상태을 눈으로 쉽게 파악할 수 있습니다.

우리는 React에서 기본적으로 제공하는 Hook이 무엇이 있는지 확인해보고, 재활용은 어떻게 할 수 있는지 확인해보는 시간을 갖도록 하겠습니다.

useState

useState는 class형 컴포넌트에서 사용하는 setState 함수와 비슷한 역할을 합니다.
useState의 두 개의 요소를 리턴합니다. 첫 번째 요소는 현재 상태, 두 번째 요소는 그 상태를 갱신할 수 있는 함수를 리턴합니다.

아래 예제를 한번 살펴보겠습니다.


// Class형 컴포넌트
export default class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      score: 0 // 초기상태설정
    };
  }

  render() {
    const { score } = this.state;
    return (
      <div>
        this is current score: {score}
        <br />
        <button
          type="button"
          onClick={() => this.setState({ score: score + 1 })}
        >
          +
        </button>
        <button
          type="button"
          onClick={() => this.setState({ score: score - 1 })}
        >
          -
        </button>
      </div>
    );
  }
}

// 함수형 컴포넌트
const App = () => {
  const [score, setScore] = useState(0); // 초기상태설정

  return (
   <div>
     this is current score: {score}
     <br/>
    <button type="button" onClick={() => setScore(score + 1)}>+</button>
    <button type="button" onClick={() => setScore(score - 1)}>-</button>
   </div>
 )
}

클래스형 컴포넌트의 상태 관리와 Hook을 사용한 상태 관리를 위해 구분해서 작성했습니다.

클래스형 컴포넌트의 상태 초기값 설정은 생성자에서 진행하지만, useState Hook을 사용하면 useState(0)와 같이 인자에 넘겨주는 값이 초기값으로 지정됩니다. 여기서는 초기값이 0입니다.
setScore는 score를 갱신할 수 있는 함수를 의미합니다. 즉, setState와 비슷한 역할을 합니다. useState을 사용하면 변수와 함수명은 본인의 입맛에 맞게 아님 회사의 입맛에 맞게 작성하면 됩니다.
+, - 버튼을 클릭하면 현재 score 상태에 +1 또는 -1을 하고 setScore 인자로 넘겨주면 상태는 갱신되고 현재의 score는 화면에 나타납니다.

useEffect

useEffect hook은 함수형 컴포넌트 내부에서 side effect를 수행할 수 있습니다. 그렇다면 side effect는 무엇일까요?
상태 업데이트, 데이터 Fetch, DOM 수정 등이 side effect라고 할 수 있습니다.
우리는 이미 class 형 컴포넌트에서도 이와 같은 방식을 사용했습니다. 한번 위의 예제를 가져와 side effect를 만들어보겠습니다.

export default class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      score: 0, // 초기상태설정
      description: ""
    };
  }

  componentDidUpdate(prevProps, prevState) {
    const { score } = this.state;

    if (prevState.score !== score) {
      this.setState({
        description: `점수가 변경되었습니다. 제점수는요? ${score}입니다.`
      });
    }
  }

  render() {
    const { score, description } = this.state;
    return (
      <div>
        this is current score: {score}
        <br />
        {description}
        <br />
        <button
          type="button"
          onClick={() => this.setState({ score: score + 1 })}
        >
          +
        </button>
        <button
          type="button"
          onClick={() => this.setState({ score: score - 1 })}
        >
          -
        </button>
      </div>
    );
  }
}

componentDidUpdate 생명 주기 메서드를 사용해 score가 업데이트될 때마다 description 상태의 값을 업데이트했습니다.
componentDidMount 등 생명 주기 메서드를 통해 rendering 이후에도 side effect를 만들 수 있죠.

우리는 위 예제를 함수형 컴포넌트의 useEffect 사용하여 이와 동일하게 작동할 수 있도록 수정하겠습니다.

// 함수형 컴포넌트
const App = () => {
  const [score, setScore] = useState(0); // 초기상태설정
  const [description, setDescription] = useState(""); // 초기상태설정

  useEffect(() => {
    setDescription(`점수가 변경되었습니다. 제점수는요? ${score}입니다.`);
  }, [score]);

  return (
    <div>
      this is current score: {score}
      <br />
      {description}
      <br />
      <button type="button" onClick={() => setScore(score + 1)}>
        +
      </button>
      <button type="button" onClick={() => setScore(score - 1)}>
        -
      </button>
    </div>
  );
};

useEffect 내부를 확인해보면 첫 번째 인자에는 함수를 할당했고, 두 번째 인자에는 배열을 할당했습니다.
첫 번째 인자는 작동시킬 함수를 할당합니다. 두 번째 인자의 배열의 값은 해당 effect의 dependency을 의미합니다.

즉, 배열의 값이 변할 경우에만 첫 번째 인자의 함수가 동작하는 것이죠. 우리는 score를 배열에 할당했습니다.

따라서, score가 갱신될 때 첫 번째 인자의 함수가 작동하여 description의 값이 갱신하게 되는 것입니다.
만약 score의 값이 변경하지 않는다면 이 effect는 동작하지 않습니다.

아래와 같은 동작을 하게 되는 것입니다. 어때요? 굉장히 쉽죠?

componentDidUpdate(prevProps, prevState) {
    const { score } = this.state;

    if (prevState.score !== score) {
      this.setState({
        description: `점수가 변경되었습니다. 제점수는요? ${score}입니다.`
      });
    }
  }

만약 우리가 componentDidMount 메서드와 같이 마운트 이후 한 번만 실행시키고 싶다면, 우리는 useEffect(() => {}, [])와 같이 두 번째 인자에 빈 배열을 할당하면 됩니다.

다음 시간에는 Hook를 사용하면서 필요한 규칙에 대해 알아보겠습니다.

Comments