Home React 09. Hooks
Post
Cancel

React 09. Hooks

Hooks는 React 16.8버전에 새로 추가되었다. Hooks은 React class 없이 state를 사용할 수 있다.

State Hook

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    import React, { useState } from 'react';

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

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

useState는 Hook의 하나이다. Hook을 통해 함수형 컴포넌트 안에서 state를 추가했다. useState는 현재 값과 업데이트하는 함수를 쌍으로 제공한다. 위 코드에서 useState는 비구조화 할당을 이용한 방법이다. 비구조화 할당은 별로도 내용을 정리해서 포스팅하자 useState는 초기값을 인자로 받아 state와 setState() 함수를 반환한다. useState의 반환값을 비구조화 할당을 통해 state는 const에 담고, setState()는 setCount에 담는다.

Hook은 함수 컴포넌트에서 React state와 생명주기를 연동할 수 있게 해주는 함수이다. Hook은 class 안에서는 동작하지 않는다. 대신 class 없이 React를 사용할 수 있게 해주는 것이다.

Effect Hook

React 컴포넌트 안에서 데이터를 가져오거나 구독하고, DOM을 직접 조작하는 작업.. 이런 모든 동작을 side effects라고 한다. 이런 동작이 다른 컴포넌트에 영향을 줄 수도 있고, 렌더링 과정에서는 구현할 수 없는 작업이기 때문이다. useEffect는 함수 컴포넌트 내에서 이런 side effects를 수행할 수 있게 해준다. React class의 componentDidMount나 componentDidUpdate, componentWillUnmount와 같은 목적으로 제공되지만, 하나의 API로 통합된 것이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    import React, { useState, useEffect } from 'react';

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

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

useEffect를 사용하면, React는 DOM을 바꾼 뒤에 “effect”함수를 실행한다. useState에서 state가 변하면 컴포넌트가 리렌더링되고, 컴포넌트가 리렌더링되면 useEffect가 실행되는 것이다. 기본적으로 React는 매 렌더링 이후에 effects를 실행한다. 첫 번째 랜더링을 포함해서…

Effect를 해제할 필요가 있다면, 해제하는 함수를 반환해주면 된다. 해제하는 함수는 “clean up”함수라 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    function FriendStatusWithCounter(props) {
        const [count, setCount] = useState(0);
        useEffect(() => {
            document.title = `${count} times`;
        });

        const [isOnline, setIsOnline] = useState(null);
        useEffect(() => {
            ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
            
            // 요게 clean up 함수
            return () => {
                ChatAPI.unscribeToFriendStatus(props.friend.id, handleStatusChange);
            };
        });

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

Hook을 사용하면 서로 관련있는 코드를 한군데 모아 작성할 수 있다. 반면 class 컴포넌트에서는 lifecycle methods 각각에 쪼개서 넣어야 한다.

Hook 사용 규칙

Hook은 Javascript 함수이지만, 두 가지 규칙을 준수해야 한다.

  • 최상위에서만 Hook을 호출해야 한다. 반목문, 조건문, 중첩함수 내에서는 Hook을 실행하지 말 것
  • React 함수 컴포넌트 내에서만 Hook을 호출해야 한다.

Hook 만들기

개발 중 상태 관련 로직을 컴포넌트 간에 재사용하고 싶을 경우 전통적으로 higher-order components와 render props를 이용해서 해결한다. Custom Hook은 이 둘과 달리 컴포넌트 트리에 새 컴포넌트를 추가하지 않고 해결할 수 있다. 위 코드에서 친구 접속상태를 확인하는 로직을 재사용한다는 가정을 하고 해당 로직을 useFriendStatus로 만들어보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    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);
            };
        });

        return isOnline;
    }

위 코드는 friendID를 받아서 친구의 접속상태를 반환한다. 이것을 여러 컴포넌트에서 사용할 수 있다.

1
2
3
4
5
6
7
8
    function FriendStatus(props) {
        const isOnline = useFriendStatus(props.friend.id);

        if (isOnline === null) {
            return 'Loading...';
        }
        return isOnline ? 'Online' : 'Offline';
    }
1
2
3
4
5
6
7
8
9
    function FriendListItem(props) {
        const isOnline = useFriendStatus(props.friend.id);

        return (
            <li style=>
            {props.friend.name}
            </li>
        );
    }

위 코드에서 각 컴포넌트의 state는 완전히 독립적이다. Hook은 state 그 자체가 아니라, 상태 관련 로직을 재사용하는 방법이다. 따라서 각각의 Hook 호출은 독립된 state를 가진다. 그래서 한 컴포넌트 내에서 같은 custom Hook을 두 번 쓸 수도 있다.Custom Hook은 별도로 한번 공부해 보자.

This post is licensed under CC BY 4.0 by the author.