Hooks at a Glance[React]

SnowCat·2023년 1월 16일
0

React - Hooks

목록 보기
1/7
post-thumbnail

※ 공식문서를 읽고 정리한 글입니다.

전에 Thinking in react 게시글에서 FilterableProductTable 컴포넌트 코드를 다음과 같이 작성했었다.

function FilterableProductTable({ json }) {
    const [searchData, setSearchData] = useState({
        filterText: "",
        inStockOnly: false,
    });
    const setTextFilter = (text) => {
        setSearchData({ filterText: text, inStockOnly: searchData.checked });
    };
    const setBoxFilter = (checked) => {
        setSearchData({
            filterText: searchData.filterText,
            inStockOnly: checked,
        });
    };
    return (
        <div>
            <SearchBar
                searchData={searchData}
                setText={setTextFilter}
                setBox={setBoxFilter}
            />
            <ProductTable json={json} searchData={searchData} />
        </div>
    );
}
  • 이를 Class를 사용한 컴포넌트로 바꿔보자
//hook 관련 import 삭제
import React from "react";

class FilterableProductTable extends React.Component {
    constructor(props) {
        super(props);
        this.setTextFilter = this.setTextFilter.bind(this);
        this.setBoxFilter = this.setBoxFilter.bind(this);
        this.state = {
            filterText: "",
            inStockOnly: false,
        };
    }
    setTextFilter(text) {
        this.setState({
            ...this.state,
            filterText: text,
        });
    }
    setBoxFilter(checked) {
        this.setState({
            ...this.state,
            inStockOnly: checked,
        });
    }
    render() {
        const { json } = this.props;
        const searchData = this.state;
        return (
            <div>
                <SearchBar
                    searchData={searchData}
                    setText={this.setTextFilter}
                    setBox={this.setBoxFilter}
                />
                <ProductTable json={json} searchData={searchData} />
            </div>
        );
    }
}

작성한 코드를 FilterableProductTable에 복붙하고 react 컴포넌트를 import하면 그대로 애플리케이션이 동작한다. 하지만..

  • 눈으로 대충봐도 코드가 매우 길어진거를 확인할 수 있다.
  • this가 매우 많이 사용되어 헷갈림을 유발하고 있다.
  • state 로직만을 전달할 때 prop을 사용해야 한다 -> 사용시 컴포넌트를 재구성해야하고 코드 추적이 어려워진다
  • 예시에서는 없지만 마운트시 이벤트가 발생하는 경우 생명주기에 맞춘 코드 구현을 강요하게 된다 -> 연관성이 없는 코드들의 결합되는 문제점이 생긴다.

따라서 현재는 Hook을 사용해 state 관리를 하는 것을 권장하고 있다.

Hooks?

  • 함수 컴포넌트에서 React state와 생명주기 기능을 연동 할 수 있게 해주는 함수
  • Hook을 통해 class없이 React 사용 가능!
  • class에서는 Hook 사용 불가능

useState()

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는 현재의 state값과 값을 업데이트 하는 함수를 제공하며, 이를 이벤트 핸들러나 다른 곳에서 호출 가능
  • 클래스에서의 this.state, this.setState와 거의 비슷하지만, 이전의 state와 새로운 state를 합쳐주지는 않음
  • 클래스와 다르게 state는 반드시 객체일 필요는 없음
  • 컴포넌트 내에서 state hook을 여러개 사용할 수도 있음

useEffect()

function FriendStatusWithCounter(props) {
  const [count, setCount] = useState(0);
  useEffect(() => {
    document.title = `You clicked ${count} times`;
  }, [count]);

  const [isOnline, setIsOnline] = useState(null);
  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  }, [props.friend.id]);

  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }
  // ...
  • useEffect를 사용해 컴포넌트 내에서 side effects 수행 가능
    side effects(=effects): 리액트 컴포넌트 안에서 데이터를 가져오거나 구독하고 DOM을 조작하는 작업
  • 클래스 컴포넌트에서의 componentDidMount, componentDidUpdate, componentWillUnmount를 통합함
  • useEffect 내부 코드는 렌더링 이후 effects를 실행하고 필요한 경우 return을 통해 effect를 해제할 수 도 있음
  • useState처럼 여러개의 useEffect 사용 가능

Hook의 재활용

import React, { useState, useEffect } from 'react';

function useFriendStatus(friendID) {
  const [isOnline, setIsOnline] = useState(null);

  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }

  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
    };
  }, [friendID]);

  return isOnline;
}

function FriendStatus(props) {
  const isOnline = useFriendStatus(props.friend.id);

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}

function FriendListItem(props) {
  const isOnline = useFriendStatus(props.friend.id);

  return (
    <li style={{ color: isOnline ? 'green' : 'black' }}>
      {props.friend.name}
    </li>
  );
}
  • custom hook을 담은 함수를 만들어 다른 컴포넌트에서 이를 재활용할 수 있음
  • 예제에서는 useFriendStatus이라는 custom Hook을 FriendStatus, FriendListItem에 각각 사용하고 있음
  • 각각의 Hook 호출은 독립된 state를 가지며, 한 컴포넌트 내부에 동일한 hook을 여러번 사용할수도 있음

Hook 사용 규칙

  • Hook은 최상위에서만 호출해야함 (반복문, 조건문, 중첩함수 내에서 실행하지 말 것)
  • React function component, Custom Hook에서만 Hook을 호출해야함 (일반 js함수에서 Hook을 호출하지 말 것)

출처:
https://ko.reactjs.org/docs/hooks-intro.html
https://ko.reactjs.org/docs/hooks-overview.html

profile
냐아아아아아아아아앙

0개의 댓글