매일은 아니더라도 꾸준하게

개발 관련 학습/React

[처음 만난 리액트] 섹션 7. Hooks

잡식성 개발자 2023. 3. 10. 17:40
728x90
반응형

Hooks의 개념과 useState, useEffect

Hook

  • 갈고리

    • 이름 앞에 use를 붙어야 함

useState

  • state를 사용하기 위한 Hook

    import React, { useState } from "react";
    
    function Counter(props) {
            var count = 0;
    
            return (
                    <div>
                            <p>총 {count}번 클릭했습니다.</p>
                            <button onClick={() => count++}>
                                    클릭
                            </button>
                    </div>
            );
    } 
  • useState() 사용법

    const [변수명, set함수명] = useState(초기값);
    import React, { useState } from "react";
    
    function Counter(props) {
            const [count, setCount] = useState(0); // setCount : 변수 각각에 대해 set 함수가 따로 존재
    
            return (
                    <div>
                            <p>총 {count}번 클릭했습니다.</p>
                            <button onClick={() => setCount(count + 1)}>
                                    클릭
                            </button>
                    </div>
            );
    } 

useEffect()

  • Side effect를 수행하기 위한 Hook

  • 리액트에서 Side effect = 효과, 영향

    • 일반적인 Side effect = 부작용
  • 다른 컴포넌트에 영향을 미칠 수 있으며, 렌더링 중에는 작업이 완료될 수 없기 때문

  • 리액트의 함수 컴포넌트에서 Side effect를 실행할 수 있게 해주는 Hook

  • useEffect() 사용법

    useEffect(이펙트 함수, 의존성 배열);
    • Effect function이 mount, unmount 시에 단 한 번씩만 실행됨

      useEffect(이펙트 함수, []);
    • 의존성 배열을 생략하면 컴포넌트가 업데이트 될 때마다 호출됨

      import React, { useState, useEffect } from "react";
      
      function Counter(props) {
              const [count, setCount] = useState(0);
      
              // componentDidMount, componentDidUpdate와 비슷하게 작동합니다.
              useEffect(() => {
                      // 브라우저 API를 사용해서 documnet의 title을 업데이트합니다.
                      document.title = `You clicked ${count} times`;
              });
      
              return (
                      <div>
                              <p>총 {count}번 클릭했습니다.</p>
                              <button onClick={() => setCount(count + 1)}>
                                      클릭
                              </button>
                      </div>
              );
      } 
      import React, { useState, useEffect } from "react";
      
      function UserStatus(props) {
              const [count, setIsOnline] = useState(null);
      
              function handleStatusChange(status) {
                      setIsOnline(status.inOnline);
              }
      
              useEffect(() => {
                      ServerAPI.subscribeUserStatus(props.user.id, handleStatusChange);
                      return () => {
                              ServerAPI.unsubscribeUserStatus(props.user.id, handleStatusChange);
                      };
              });
      
              if (isOnline === null) {
                      return '대기 중...';
              }
              return isOnline ? '온라인' : '오프라인';
      function UserStatusWithCounter(props) {
              const [count, setCount] = useState(0);
              useEffect(() => {
                      document.title = `총 ${count}번 클릭했습니다.`;
              });
      
              const [isOnline, setIsOnline] = useState(null);
              useEffect(() => {
                      ServerAPI.subcribeUserStatus(props.user.id, handleStatusChange);
                      return () => {
                              ServerAPI.subcribeUserStatus(props.user.id, handleStatusChange);
                      };
              });
      
              function handleStatusChange(status) {
                      setIsOnline(status.isOnline);
              }
      
              // ...
      useEffect(() => {
              // 컴포넌트가 마운트 된 이후,
              // 의존성 배열에 있는 변수들 중 하나라도 값이 변경되었을 때 실해오딤
              // 의존성 배열에 빈 배열([])을 넣으면 마운트와 언마운트시에 단 한 번씩만 실행됨
              // 의존성 배열 생략 시 컴포넌트 업데이트 시마다 실행됨
              ...
      
              return () => {
                      // 컴포넌트가 마운트 해제되기 전에 실행됨
                      ...
              }
      }, [의존성 변수1, 의존성 변수2, ...]);

useMemo, useCallback, useRef


useMemo()

  • Memoized value를 리턴하는 Hook

  • Memoization : 연산량이 많이 드는 함수의 호출 결과를 저장해 두었다가, 같은 입력값으로 함수를 호출하면 새로 함수를 호출하지 않고, 이전에 저장해 두었던 호출 결과를 반환하는 것.

  • 사용법

    const memoizedValue = useMemo(
            () => {
                    // 연산량이 높은 작업을 수행하여 결과를 반환
                    return computeExpensivaValue(의존성 변수1, 의존성 변수2);
            },
            [의존성 변수1, 의존성 변수2]
    );
    
    // 렌더링이 일어나는 동안 실행됨
  • 의존성 배열을 넣지 않을 경우, 매 렌더링마다 함수가 실행됨

    const memoizedValue = useMemo(
            () => computeExpensiveValue(a, b)
    );
  • 의존성 배열이 빈 배열일 경우, 컴포넌트 마운트 시에만 호출됨

    const memoizedValue = useMemo(
            () => {
                    return computeExpensiveValue(a, b);
            },
            []
    );

useMemo, useCallback, useRef

  • useMemo() Hook과 유사하지만 값이 아닌 함수를 반환

  • 사용법

    const memoizedCallback = useCallback(
            () => {
                    do Something(의존성 변수1, 의존성 변수2);
            },
            [의존성 변수1, 의존성 변수2]
    );
    
    // 의존성 배열의 값이 바뀐 경우에만 함수를 새로 정의해서 리턴
    // 함수와 의존성 배열을 파라미터로 받음
    // 파라미터로 받는 함수를 콜백이라고 부름
    // 의존성 배열에 있는 변수 중 하나라도 바뀌면 메모이제이션된 콜백함수를 반환
    // 의존성 배열에 따라 메모이즈드 된 값을 반환하는 것은 useMemo()와 동일
  • 동일한 역할을 하는 두줄의 코드

    useCallback(함수, 의존성 배열);
    useMemo(() => 함수, 의존성 배열);
    import { useState } from "react";
    
    function ParentComponent(props) {
        const |count, setCount| = useState(0);
    
        // 재렌더링 될 때마다 매번 함수가 새로 정의됨
        const handleClick = (event) => {
            // 클릭 이벤트 처리
        };
    
        return (
            <div>
                <button
                    onClick={() => {
                        setCount(count + 1);
                    }}    
                >
                    {count}
                </button>
    
                <ChildComponent handleClick={handleClick} />
            </div>
        );
    }
    import { useState } from "react";
    
    function ParentComponent(props) {
        const |count, setCount| = useState(0);
    
        // 재렌더링 될 때마다 매번 함수가 새로 정의됨
        const handleClick = (event) => {
            // 클릭 이벤트 처리
        };
    
        return (
            <div>
                <button
                    onClick={() => {
                        setCount(count + 1);
                    }}    
                >
                    {count}
                </button>
    
                <ChildComponent handleClick={handleClick} />
            </div>
        );
    }

=======

실습. Hooks 사용해보기

useCounter Hook 만들기

import React, { useState } from "react";

function useCounter(initialValue) {
    const [const, 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;

// useCounter Hook은 초기 카운트 값을 파라미터로 받아서, 
// count라는 이름의 state를 생성하여 값을 제공하고,
// count 증가 및 감소를 편리하게 할 수 있도록 함수를 제공하는 Hook이다.

Accommodate 컴포넌트 만들기

import React, { useState, useEffect } from "react";
import useCounter from "./useCounter";

const MAX_CAPACITY = 10; // 최대 수용 인원

function Accomodate(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 Click={decreaseCount}>
                퇴장
            </button>

            {isFull && <p style={{ color: "red" }}>정원이 가득찼습니다.</p>}
        </div>
    );
}
  • 앞에서 만든 useCounter()를 사용하여 count를 관리한다.
  • 두 개의 useEffect훅을 사용함 → 의존성 배열 유무
  • 의존성 배열이 없는 형태는 컴포넌트가 마운트된 직후 호출, 이 후 컴포넌트가 업데이트 될 때마다 호출
  • 의존성 배열이 있는 형태는 컴포넌트가 마운트된 직후 호출, 이 후 카운트 값이 바뀔 때마다 호출, 용량이 가득 찼는지 아닌지를 isFull에 저장
728x90