ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [ReactJS] Hooks
    Programming/ReactJS 2022. 11. 30. 14:04

    Hooks

    Component에는 함수, 클래스 컴포넌트 이렇게 총 두 종류가 있는데, 그 중 Hooks 같은 경우 함수 컴포넌트에 해당된다. 함수 컴포넌트는 state 사용이 불가능하고, Lifecycle에 따른 기능 구현이 불가능하다는 특징이 있다. 하지만 Hooks을 사용한다면 함수 컴포넌트에서도 클래스 컴포넌트의 기능을 사용할 수 있게 된다. 이름 앞에 use를 붙여 Hooks임을 나타내주면 커스텀 훅으로도 사용이 가능하다.

    useState

    함수 컴포넌트에서도 State를 사용할 수 있게 해주는 훅이다. 사용 방법은 다음과 같다.

    const [변수명, set함수명] = useState(초기값);

    이를 실제로 활용하는 예제코드이다. 카운트가 증가하면 컴포넌트가 재랜더링 되는데, useState 같은 경우 변수 각각에 대해 set함수가 따로 존재한다는 특징이 있다.

    import React, {useState} from "react";
    
    function Counter(props) {
        const [count, setCount] = useState(0);
    
        return (
            <div>
                <p>총 {count} 번 클릭했습니다.</p>
                <button onClick={() => setCount(count + 1)}>
                    클릭
                </button>
            </div>
        )
    }

    useEffect

    Side effect를 수행하기 위한 용도로 활용된다. 서버에서 데이터를 받아오거나 수동으로 DOM을 받아오는 과정 등이 있다. 생명주기 함수(didmount, didupdate, willunmount)의 기능을 하나로 사용할 수 있다. 사용 방법은 다음과 같다. 의존성 배열에 있는 변수들 중 하나라도 값이 변경되었을 때 실행된다.

    useEffect(이펙트 함수, 의존성 배열);

    만약 Effect function이 mount, unmount 시에 단 한번씩만 실행되게 하려면 다음과 같이 의존성 배열을 비워주면 된다.

    useEffect(이펙트 함수, []);

    useEffect에서 사용되는 return 함수는 컴포넌트가 unmount 될 때 호출되는데, 즉슨 componentWillUnmount() 생명주기 함수와 동일한 역할을 수행한다고 생각하면 된다.

    useEffect(() => {
        ServerAPI.subscribeUserStatus(props.user.id, handleStatusChange);
        return () => {
            ServerAPI.unsubscribeUserStatus(props.user.id, handleStatusChange);
        };
    });

    useMemo

    memoized value를 return하는 Hook이다. 최적화를 위해 사용되는데, 비용이 높은 함수의 호출 결과를 미리 저장해두었다가 같은 입력값을 받는 경우 이전에 기억했던 값을 그대로 반환해주는 것이다. 사용 방법은 다음과 같다.

    const memoizedValue = useMemo(
        () => {
            return computeExpensiveValue(의존성 변수1, 의존성 변수2);
        },
        [의존성 변수1, 의존성 변수2]
    );

    빠른 랜더링 속도를 얻을 수 있다는 장점이 있다. useMemo에 랜더링이 일어나는 동안 진행되는 작업들이 사용되어서는 안된다.

    useCallback

    useMemo와 유사하지만 값이 아닌 함수를 반환한다. 사용 방법은 다음과 같다.

    const memoizedCallback = useCallback(
        () => {
            doSomething(의존성 변수1, 의존성 변수2);
        },
        [의존성 변수1, 의존성 변수2]
    );
    const handleClick = (event) => {
        // 클릭 이벤트 처리
    };
    
    const handleClick = useCallback((event) => {
        // 클릭 이벤트 처리
    }, []);

    useRef

    특정 컴포넌트에 접근할 수 있는 객체이다. 변경가능한 current를 가진 하나의 상자라고 생각하면 된다. 자바스크립트 객체를 반환할 수 있다, 매번 랜더링 될 때마다 같은 객체를 반환한다는 특징이 있다. (재랜더링 X)

    const refContainer = useRef(초깃값);

    Hook의 규칙

    1. Hook은 무조건 최상위 레벨에서만 호출해야 한다. 컴포넌트가 랜더링 될 때마다 매번 같은 순서로 호출되어야 한다.

    2. Hook은 리액트 함수 컴포넌트에서만 Hook을 호출해야 한다.

    3. eslint-plugin-react-hooks 플러그인을 활용하면 Hook의 규칙을 잘 따르고 있는지 아닌지를 분석할 수 있다.

    Custom Hook 만들기

    function UserStatus(props) {
        const [isOnline, setIsOnline] = useState(null);
    
        useEffect(() => {
            function handleStatusChange(status){
                setIsOnline(status.isOnline);
            }
    
            ServerAPI.subscribeUserStatus(props.user.id, handleStatusChange);
            return () => {
                ServerAPI.unsubscribeUserStatus(props.user.id, handleStatusChange);
            }
        })
    
        return (
            <li style = {{color: isOnline ? 'green' : 'black'}}>
                {props.user.name}
            </li>
        )
    }

    다음과 같은 코드를 Custom Hook을 통하여 재구성하기 위해서 새롭게 UserStatus와 UserListItem 훅을 만들었다.

    function useUserStatus(userId) {
        const [isOnline, setIsOnline] = useState(null);
    
        useEffect(() => {
            function handleStatusChange(status) {
                setIsOnline(status.isOnline);
            }
    
            ServerAPI.subscribeUserStatus(userId, handleStatusChange);
            return () => {
                ServerAPI.unsubscribeUserStatus(userId, handleStatusChange);
            }
        })
    
        return isOnline;
    }
    
    function UserListItem(props) {
        const isOnline = useUserStatus(props.user.id);
    
        return (
            <li style={{color: isOnline ? 'green' : 'black'}}>
                {props.user.name}
            </li>
        )
    }

    < Source />

    useCounter.jsx

    import React, { useState } from "react";
    
    function useCounter(initialValue) {
        const [count, setCount] = useState(initialValue);
    
        const increaseCount = () => setCount((count) => count + 1);
        const decreaseCount = () => setCount((count) => Math.max(count -1, 0));
    
        return [count, increaseCount, decreaseCount];
    }
    
    export default useCounter;

    Accommodate.jsx

    import React, { useEffect, useState } from "react";
    import useCounter from "./useCounter";
    
    const MAX_CAPACITY = 10;
    
    function Accommodate(props) {
        const [isFull, setIsFull] = useState(false);
        const [count, increaseCount, decreaseCount] = useCounter(0);
    
        useEffect(() => {
            console.log("===================");
            console.log("useEffect() is called");
            console.log(`isFull: ${isFull}`);
        });
    
        useEffect(() => {
            setIsFull(count >= MAX_CAPACITY);
            console.log(`Current count value: ${count}`);
        }, [count]);
    
        return (
            <div style = {{ padding: 16}}>
                <p>{`총 ${count}명 수용했습니다.`}</p>
    
                <button onClick={increaseCount} disabled = {isFull}>
                    입장
                </button>
                <button onClick={decreaseCount}>퇴장</button>
    
                {isFull && <p style={{ color: "red" }}>정원이 가득 찼습니다.</p>}
            </div>
        );
    }
    
    export default Accommodate;

    ※ 본 게시글은 소플님의 강의 영상을 참고하여 작성되었습니다. 개인적인 공부 목적으로 사용하고 있고, 문제 시 비공개 전환하도록 하겠습니다.

    'Programming > ReactJS' 카테고리의 다른 글

    [ReactJS] Conditional Rendering  (0) 2022.11.30
    [ReactJS] Handling Events  (0) 2022.11.30
    [ReactJS] State and Lifecycle  (0) 2022.11.30
    [ReactJS] Components and Props  (0) 2022.11.30
    [ReactJS] Rendering Elements  (0) 2022.11.29

    댓글