1. React Hooks란 무엇인가?

  • React의 새로운 기능으로, 함수형 컴포넌트에서도 클래스형 컴포넌트에서만 가능했던 상태 관리(state management)라이프사이클(lifecycle)을 사용할 수 있도록 해주는 기능이다.
  • Hooks는 클래스형 컴포넌트를 사용하는 대신, 더 간단하고 가독성이 좋은 함수형 컴포넌트를 작성할 수 있게 돕는다.

함수형 컴포넌트는 기본적으로 리렌더링 시 함수안에 작성된 모든 코드가 다시 실행된다. 즉, 함수형 컴포넌트가 리렌더링될 때는 무조건 새롭게 컴포넌트 함수가 선언되고, state가 초기화되며, 메모리에 할당된다.

그렇기 때문에 함수형 컴포넌트에서는 기존에 가지고 있던 상태를 관리할 수 없었다. 하지만 hook이 브라우저에 매모리를 할당해 저장하게 되면서 함수형 컴포넌트에서도 상태 관리가 가능하게 되었다.


2. Hook 사용 규칙

  1. 최상위 단계에서만 호출해야 한다.

    • 반복문, 조건문, 중첩된 함수 내부에서 호출하면 안 된다.

    react는 Hook들이 호출되는 순서를 저장한다. 그리고 매 렌더링마다 순서대로 Hook을 호출하는데, 만약 조건문이나, 반복문, 중첩 함수 내에서 어떠한 이유로 어떤 Hook이 호출되지 않을 경우 React가 기억하고 있는 Hook의 순서와 실행되는 Hook의 순서와 실행되는 Hook들의 순서가 달라져 버그 가능성이 높아지게 된다.

    따라서 최상위 단계에서만 호출해야 하므로 항상 동일한 순서로 Hook이 호출되는 것이 보장된다.

  2. React 함수형 컴포넌트 내에서만 호출 가능하다.

    • 일반적인 JavaScript 함수에서 호출하면 에러가 발생한다. 왜냐하면 Hook이라는 기능 자체가 React의 함수형 컴포넌트 내에서 사용되기 위해 만들어진 메서드이기 때문이다.
  3. 커스텀 훅의 이름은 use로 시작해야 한다.

    • 예: useScroll, useToggle 관례에 가까운 규칙이지만 다른 개발자가 볼 때도 Custom Hook이라는것을 알아 볼 수 있다.

    🔗 공식문서 확인하기


3. Hooks 종류와 설명

  1. useState()

    • 상태를 관리하기 위한 가장 기본적인 Hook이다.
    • 사용 예: const [state, setState] = useState(initialValue);
  2. useRef()

    • DOM에 직접 접근하거나 컴포넌트 간 상태를 보존할 때 사용하는 Hook이다.
    • 특정 DOM 요소에 안전하게 접근하거나 리렌더링 시에도 값을 유지하는 지역변수가 필요한 경우에 사용한다.
    • 예: 특정 DOM 요소를 참조하거나 리렌더링을 방지하는 값 저장.
    const inputElement = useRef();
    
    <input type="text" ref={inputElement} />

    useRef Hook을 통해 참조를 생성한다. 그리고 참조할 요소를 ref 속성으로 연결한다.

    예 ) 특정 DOM 요소에 접근하기

    import { useRef, useEffect } from 'react';
    
    export default function AccessDom() {
      const inputRef = useRef();
    
      useEffect(() => {
        // Javascript의 명령형 코드를 사용해 DOM 요소에 접근하면 React의 선언적 특성에 위배
        // 직접 DOM을 조작하면 React의 가상 DOM과 실제 DOM 간의 상태가 일치하지 않을 수 있음
        const inputElement = document.getElementById('myInput');
        inputElement.focus();
    
        // 컴포넌트가 Mount된 후에 input 요소에 접근
        // inputRef는 단순히 참조 객체이기 때문에 DOM 요소에 접근하기 위해 inputRef.current 사용
        inputRef.current.focus();
      }, []);
    
      return (
        <>
          {/* document.getElementById로 DOM 요소에 접근하는 좋지 않은 방식 */}
          <input id='myInput' type='text' />
    
          {/* useRef Hook을 사용한 안전한 DOM 접근 방식 */}
          <input ref={inputRef} type='text' />
        </>
      );
    }
    const variable = useRef(초기값);

    그리고 일반적인 지역 변수가 필요한 경우에는 useRef Hook에 인자로 초기값을 작성한다. 이 useRef로 만들어진 참조 변수는 리렌더링이 발생해도 계속 유지된다.

    예 ) useRef ,useState,일반 변수의 상태 관리와 DOM 접근의 차이

    import React, { useRef, useState } from "react";
    
    export default function RefFunction2() {
      const idRef = useRef(1);
      const [id, setId] = useState(10);
      let test = 10;
    
      const plusIdRef = () => {
        idRef.current += 1;
        console.log(idRef.current);
        test += 1;
        console.log(test);
      };
      const plusIdState = () => {
        setId(id + 1);
      };
      return (
        <div>
          <h1>Ref Sample</h1>
          <h2>{test}</h2>
          <h2>{idRef.current}</h2>
          <button onClick={plusIdRef}>PLUS Ref</button>
          <h2>{id}</h2>
          <button onClick={plusIdState}>PLUS State</button>
        </div>
      );
    }

    코드 설명: useRefuseState 비교

    이 코드는 React의 useRefuseState를 활용하여 상태 관리와 DOM 접근의 차이를 보여줍니다. 각각의 사용 방식과 동작 방식을 이해하기 쉽게 설명하겠습니다.


    주요 구성 요소

    1. useRef
      • idRef로 선언되어 있으며, idRef.current를 통해 값을 저장하고 접근한다.
      • useRef는 값이 변경되어도 컴포넌트를 다시 렌더링하지 않는다.
      • 주로 DOM 접근과 값 저장에 사용된다.
    2. useState
      • id로 선언되어 있으며, setId를 통해 상태를 업데이트한다.
      • useState 값이 변경되면 컴포넌트를 다시 렌더링한다.
    3. test
      • 일반 변수로 선언되어 있으며, React의 상태 관리와 무관하다.
      • 값이 변경되더라도 리렌더링되지 않는다.

    코드 동작 순서

    초기 렌더링

    1. useRef의 초기값:
      • idRef.current = 1.
      • useRef 값은 렌더링과 무관하다.
    2. useState의 초기값:
      • id = 10.
      • 상태가 설정되며 컴포넌트는 이 값으로 렌더링된다.
    3. test 변수의 초기값:
      • test = 10.
      • 렌더링에 영향을 미치지 않는다.

    화면 출력:

    <h2>{test}</h2>            => 10
    <h2>{idRef.current}</h2>   => 1
    <h2>{id}</h2>              => 10

    PLUS Ref 버튼 클릭

    const plusIdRef = () => {
      idRef.current += 1;
      console.log(idRef.current);
      test += 1;
      console.log(test);
    };
    1. idRef.current 값 변경:
      • idRef.current 값이 1 증가. 예를 들어, 1 → 2.
      • 이 값은 렌더링과 무관하므로 화면은 업데이트되지 않는다.
      • 콘솔에 idRef.current의 새로운 값이 출력된다.
    2. test 변수 값 변경:
      • test 값이 1 증가. 예를 들어, 10 → 11.
      • 일반 변수이므로 리렌더링이 발생하지 않는다.
      • 콘솔에 test의 새로운 값이 출력된다.

    화면은 여전히 다음과 같다:

    <h2>{test}</h2>            => 10 (변경되지 않음)
    <h2>{idRef.current}</h2>   => 1 (변경되지 않음)
    <h2>{id}</h2>              => 10

    PLUS State 버튼 클릭

    const plusIdState = () => {
      setId(id + 1);
    };
    1. setId 호출:
      • id 값이 10 → 11로 변경된다.
      • useState는 상태가 변경되면 컴포넌트를 리렌더링한다.
      • 변경된 id 값이 렌더링된다.

    화면 업데이트:

    <h2>{test}</h2>            => 10 (변경되지 않음)
    <h2>{idRef.current}</h2>   => 1 (변경되지 않음)
    <h2>{id}</h2>              => 11 (업데이트됨)

    주요 차이점 설명

    1. useRef
      • 값을 저장하지만 리렌더링에 영향을 주지 않는다.
      • idRef.current의 변경은 단순히 메모리에 저장되는 값만 업데이트한다.
    2. useState
      • 상태가 변경되면 컴포넌트가 다시 렌더링된다.
      • 화면에 즉각적으로 반영되어야 하는 데이터에 적합하다.
    3. 일반 변수 (test)
      • 값은 변경할 수 있지만 React 렌더링 사이클과 관련이 없다.
      • 값이 변경되더라도 화면에는 반영되지 않는다.

    동작 요약

    항목렌더링 영향사용 목적코드 내 역할
    useRefX값 저장, DOM 접근idRef를 통해 값 저장
    useStateO상태 관리, 화면 업데이트id를 통해 상태 관리
    일반 변수 (test)X단순 계산, 임시 값 저장계산 중 임시 값 저장

    실행 흐름 요약

    1. 초기 렌더링:

      • useRef, useState, test 초기값이 설정되고 화면에 렌더링된다.
    2. PLUS Ref 버튼 클릭:

      • idRef.currenttest 값은 변경되지만 화면은 리렌더링되지 않는다. (특정 DOM 요소에 안전하게 접근. 리렌더링 되어도 유지되는 지역 변수)
    3. PLUS State 버튼 클릭:

      • id 값이 변경되며 컴포넌트가 리렌더링된다.

      • useRef를 통해 만든 참조 객체

        useRef를 통해 만든 참조 객체는 말 그대로 객체이기 때문에 ref 속성을 통해 inputRef 참조 객체로 연결된 input 요소에 직접 접근하려면 current 메서드를 사용해야 한다. 그러므로 input.current.focus를 통해 input 요소에 focus 설정을 할 수 있다.


  3. useEffect()

    • 컴포넌트가 렌더링될 때마다 특정 작업을 수행하도록 설정한다.
    • 예: API 호출, 이벤트 리스너 등록/해제.
      useEffect = (callback 함수, [의존성 배열]);
      함수형 컴포넌트에서 LifeCycle에 따라 발생할 이벤트들을 관리할 수 있다. 컴포넌트가 Mount, Unmount되거나 의존성 배열의 요소에 변화가 생겨 Update될 때, callback 함수가 호출된다.
  4. useMemo()

    • 계산 비용이 높은 함수의 결과를 메모이제이션하여 성능을 최적화한다.
    • 특정 의존성 값이 변경될 때만 계산이 다시 실행된다.
  5. useCallback()

    • 함수를 메모이제이션하여 불필요한 함수 재생성을 방지하고, 렌더링 성능을 최적화한다.
  6. useReducer()

    • 상태 관리 로직이 복잡할 때 useState의 대체재로 사용한다.
    • 리듀서 함수(reducer)와 디스패치(dispatch)를 통해 상태를 업데이트한다.
  7. useContext()

    • 전역 상태나 데이터를 관리하며, 컴포넌트 트리 내에서 데이터를 쉽게 전달할 수 있다.
    • 예: 테마 설정, 인증 상태.
      // Context 생성
      const MyContext = React.CreateContext(defaultValue);
      
      // Context에 값을 전달
      <MyContext.Provider value={/* 전달할 값 */} />
      
      // Context 값 사용
      const value = useContext(MyCOntext);
      CreateContext를 사용해 Context를 생성하고, Provider 컴포넌트에서 Context에 값을 전달한다. 그리고 useContext로 해당 Context의 값을 사용한다.

4. 주요 Hook의 사용 사례

  1. useMemo()

    • 이유: 렌더링 시 매번 실행되는 고비용 연산을 방지하기 위해 사용한다. ( 인자로 받은 callback 함수의 연산 결과를 저장해서 재사용할 수 있도록 최적화 시켜주는 hook )
    • 컴포넌트 리렌더링으로 인해 불필요하게 호출되는 함수의 결과값을 저장해 재사용. 두 번째 인자인 의존성 배열 요소의 값이 바뀌는 경우에만 callback 함수를 다시 실행
    • 작동 원리: 특정 값(의존 배열)이 변경될 때만 계산이 실행되며, 그렇지 않으면 이전 계산 결과를 반환한다.
      const memoizedValue = useMemo(callback 함수, [의존성 배열]);
      렌더링 과정에서 두 번째 인자로 받은 의존 배열(dependency) 내 값이
      바뀌는 경우에만 첫번째 인자로 받은 콜백함수를 실행해 구한 값을 반환한다.

    예 )

    function calc(a, b) {
    	return a + b
    }
    
    // 함수형 컴포넌트
    const MyComponent() {
    	const result = calc(3,5)
    	
    	return <p>{result}</p>

    컴포넌트가 렌더링 됨 = 함수를 호출 ⇒ 함수 내부의 모든 변수 초기화

    리렌더링 될 때 마다 MyComponent 호출,

    변수 result가 초기화 되므로 매번 calc 함수 실행!

    매번 calc되는것은 비효율적. 따라서 결과만 다져오게 끔 하도록 useMemo가 필요함.

    ⇒ useMemo를 사용하여 부하가 걸리는 함수의 결과값을 메모리에 저장한 뒤, 리렌더링이 될 때 그 결과값만 가져와서 재사용해 줌으로써 성능을 최적화

    예 )임의로 큰 연산을 하는 computeExpensiveValue 함수를 작성

    import React, { useState, useMemo } from 'react';
    
    export default function ComputeExpensive() {
      const [count, setCount] = useState(0);
      const [otherState, setOtherState] = useState(0);
    
      const computeExpensiveValue = (count) => {
        console.log('임의로 큰 연산 진행 중...');
        for (let i = 0; i < 100000; i++) {}
    
        return count ** 2;
      };
    
      // useMemo를 사용하지 않는 경우
      const expensiveValue = computeExpensiveValue(count);
    
      // useMemo를 사용하는 경우
      //   const expensiveValue = useMemo(() => computeExpensiveValue(count), [count]);
    
      return (
        <>
          <p>임의의 큰 연산 결과: {expensiveValue}</p>
          <button onClick={() => setCount(count + 1)}>count + 1</button>
          <button onClick={() => setOtherState(otherState + 1)}>
            otherState + 1
          </button>
        </>
      );
    }

    위의 코드 설명 : 콘솔을 찍어 연산을 하는 함수가 호출되었다는 것을 알리고 인자로 받은 값의 거듭제곱한 값을 반환한다. 그리고 expensiveValue변수에 computeExpensiveValue 함수의 결과값 저장하는데, useMemo를 사용하는 경우, 사용하지 않는 경우를 비교할 것이다.

useMemo를 사용하지 않은 경우는 count가 업데이트 될때와 computeExpensiveValue 함수와 관련이 없는 otherState가 업데이트 될때 모두 computeExpensiveValue 함수가 실행되는 것을 볼 수 있다.

이제 useMemo hook을 사용해보자. 이제 함수의 결과값을 저장해, count의 값에 변화가 있을때만 함수가 실행 되는것을 볼 수 있다.

count state의 값을 업데이트 할 때만 computeExpensiveValue 함수가 호출되고, otherState 의 state 값이 업데이트될 때는 useMemo hook의 의존성 배열에 작성했던 count 값에 변화가 없으니 리렌더링 되어도 computeExpensiveValue 함수가 호출되지 않는다.

  1. useCallback()

    • 이유: 매번 렌더링 시 함수가 새롭게 생성되는 것을 방지하기 위해 사용한다.
    • 함수 자체를 저장하고, 리렌더링 시 함수를 다시 선언하고 호출하는것을 방지해 최적화 한다.
    • 작동 원리: 특정 값(의존 배열)이 변경될 때만 함수를 재생성한다.
    • 차이점: useMemo는 값을 메모이제이션(값을 최적화), useCallback은 함수를 메모이제이션(다시 rendering 될 때 함수를 다시 불러오는것을 막음)한다.
      const memoizedCallback = useCallback(callback 함수, [의존성 배열]);

    의존성 배열의 요소에 변화가 있으면 callback 함수가 실행되는것은 useMemo와 같다. 그리고 useCallback에 저장할 함수가 state에 의존적이지 않은 함수라면 빈 배열을 작성해도 된다.

    예 ) 리스트를 렌더하고, 각 리스트 요소를 수정, 삭제하는 기능

    import { useState, useCallback } from 'react';
    
    export default function UseCallbackPrac() {
      const [items, setItems] = useState(['Item 1', 'Item 2', 'Item 3']);
      const [editing, setEditing] = useState(null);
      const [editText, setEditText] = useState('');
    
      // item 문자열을 인자로 받아 editing, setEditText 상태의 값을 item 문자열로 업데이트
      const handleEdit = useCallback((item) => {
        setEditing(item);
        setEditText(item);
      }, []);
    
      // itemToSave 문자열을 인자로 받아 items 상태의 배열 요소 중
      // editing과 동일한 요소만 itemToSave로 변경하고
      // 나머지 요소는 그대로 유지한 배열을 items 상태값으로 업데이트
      const handleSave = useCallback(
        (itemToSave) => {
          setItems(items.map((item) => (item === editing ? itemToSave : item)));
          setEditing(null);
        },
        [items, editing]
      );
    
      // itemToDelete 문자열을 인자로 받아 items 상태의 배열 요소 중
      // itemToDelete와 동일하지 않은 요소로만 이루어진 배열을 items 상태값으로 업데이트
      const handleDelete = useCallback(
        (itemToDelete) => {
          setItems(items.filter((item) => item !== itemToDelete));
        },
        [items]
      );
    
      return (
        <ul>
          {items.map((item) => (
            <li key={item}>
              {editing === item ? (
                <input
                  type='text'
                  value={editText}
                  onChange={(e) => setEditText(e.target.value)}
                />
              ) : (
                item
              )}
              {editing === item ? (
                <button onClick={() => handleSave(editText)}>Save</button>
              ) : (
                <>
                  <button onClick={() => handleEdit(item)}>Edit</button>
                  <button onClick={() => handleDelete(item)}>Delete</button>
                </>
              )}
            </li>
          ))}
        </ul>
      );
    }
    1. 리스트로 보여줄 items, 수정하는 리스트를 저장할 editing, 수정할 문자열을 저장할 editText state작성
    2. return문 내부 작성 최상위 요소로 ul태그로 감싸기, items를 map 메서드로 반복해서 item 요소를 보여줌, 여기서 만약 editing state가 해당 요소와 동일하다면 li 요소의 내부에 input 태그와 저장버튼이 렌더, 아니라면 item의 값과 수정, 삭제 버튼이 렌더
    3. 각 input에 button에 속성을 작성. editing값이 반복되는 items의 요소중 하나와 같다면 렌더될 input 태그의 value는 editText이고, onChange로 변화가 생기면 입력값을 editText의 값으로 업데이트 한다. 그리고 각 버튼들에는 클릭 이벤트 함수들을 작성
    4. 이제 클릭이벤트 함수들에 불필요한 재선언 호출 함수들을 모두 UseCallback으로 처리. 1. handleEdit함수 - item을 인자로 받아 item을 editing과 editing state 값으로 업데이트하는 기능을 하고 이 함수으의 경우 state의 변화와 상관 없이 기능하기 때문에 의존성 배열이 빈 배열임 2. handleSave - useCallback으로 item 배열 state의 요소를 순회하면서 editing 값과 동일한 요소만 전달받은 itemToSave 값으로 수정하는 함수를 저장. 그리고 이함수는 items와 editing state가 변화할 경우 다시 함수를 저장해야 하므로 의존성 배열에 두 state가 작성됨.
    5. handleDelete - UseCallback으로 삭제한 item의 값을 인자로 받아 items 배열 state에서 filter 메서드로 삭제할 item의 값과 동일하지 않은 요소들을 모아 items 배열을 업데이트 하는 함수를 저장. 리 함수의 경우 items의 배열에 변화가 생기면 함수를 다시 저장해야 하므로 의존성 배열에 items를 작성.
  2. useReducer()

    • 이유: 상태가 복잡하거나 여러 상태 변경 로직을 하나로 모아 관리할 필요가 있을 때 사용한다.
    • useState와 비슷하게 상태를 관리하지만 복잡한 로직을 다룰 때 더 적합하다. useState를 기반으로한 hook이고, 상태의 업데이트 로직을 컴포넌트에서 분리할 수 있다.
      const [state, dispatch] = useReducer(reducer, initialState);
    • 구성 요소:
      • state: 현재 상태

      • dispatch: 상태를 업데이트하는 함수 (액션을 발생시키는 함수-액션은 상태를 변경하기 위한 정보를 담고 있는 객체)

      • reducer: 상태 업데이트 로직을 정의한 함수 (state를 업데이트하는 함수)

      • initialState : initialState 상태의 초기값

        import React, { useReducer } from "react";
        
        const initState = { value: 0 };
        
        const reducer = (prevState, action) => {
          console.log(prevState, action);
        
          switch (action.type) {
            case "INCREMENT":
              return { value: prevState.value + 1 };
            case "DECREMENT":
              return { value: prevState.value - 1 };
            case "RESET":
              return initState;
            default:
              return { value: prevState.value };
          }
        };
        
        export default function UseReducerEx() {
          // reducer : state 를 업데이트 하는 함수
          // dispatch : 액션 (state가 어떻게 변경되어야 하는지에 대한 힌트)을 발생시키는 함수
          // state : 현재 상태
          // useReducer는 [state, dispatch] 를 리턴함
          const [state, dispatch] = useReducer(reducer, initState);
          return (
            <div>
              <h1>UseReducerEx</h1>
              <h2>{state.value}</h2>
        
              <button onClick={() => dispatch({ type: "INCREMENT" })}>Plus</button>
              <button onClick={() => dispatch({ type: "DECREMENT" })}>Minus</button>
              <button onClick={() => dispatch({ type: "RESET" })}>Reset</button>
            </div>
          );
        }
      1. initState라는 초기 상태값을 정의. 그리고 useReducer hook을 사용해 state, dispatch, reducer를 정의
      2. switch문으로 action의 값에 따라 state를 업데이트하는 방법을 다르게작성. action.type이 INCREMENT라면 기존 state의 value에서 1을 더하고, DECREMENT라면 기존 state의 value에서 1을 뺀다. 그리고 RESET이라면 initState를 state의 value로 업데이트 한다.
      3. return문 매부에 h2태그를 사용해 reducer state의 값을 보여주고, 버튼 3개를 만든다. 각 버튼을 클릭하면 dispatch 함수를 실행하는데, 인자로 type이라는 키의 값을 INCREMENT, DECREMENT, RESET이라는 객체 형태로 액션을 전달한다.
      4. 각 버튼 클릭 시 인자로 전달한 action.type에 따라 reducer함수에서 state.value의 값을 업데이트 한다.

5. useState() vs. useReducer()

  • useState()는 단순한 상태 관리에 적합하다.
    • 예: 카운터 값 증가, 단순 토글.
  • useReducer()는 상태가 복잡하거나 업데이트 로직이 다양한 경우 적합하다.
    • 예: 객체나 배열 상태 관리, 여러 액션 핸들링.
  • state가 하나 or 단순 → useState
  • state가 여러게 or 복잡 → useReducer

useReducer는 useState 기반이지만 무조건 useReducer가 좋은것은 아니다. 경우에 따라 유연하게 사용하기!


6. Custom Hooks

  • 반복되는 로직을 재사용하기 위해 사용자 정의 훅을 작성할 수 있다. ( 개발자가 직접 만드는 훅 )
  • 여러 컴포넌트에서 반복해서 사용되는 상태 로직을 하나의 함수로 만들어 사용할 수 있다.
    function useCustomHook(인자) {
      커스텀 훅 로직
    
      return 반환할 데이터
    }
  • 구성 방법:
    1. hooks/ 디렉토리에 훅을 정의.
    2. 파일 이름은 use로 시작. ( 다른 개발자가 볼 때에도 이 함수가 커스텀 훅이라는것을 알려주기 위함 )
      • 예: useScroll.js, useToggle.js
  • 장점:
    • 컴포넌트 로직 분리 가능.
    • 재사용성을 높이고 코드 중복을 줄일 수 있다.

util Function함수와의 차이점 : Custom Hooks 과 util 함수 모두 코드 중복을 줄이고, 유지보수성을 높이기 위해 사용된다. 하지만 Custom Hooks은 React Hook을 사용한 특정 동작이나 기능을 제공하는 함수이고, util 함수는 React의 Hook을 사용하지 않고 어디서든 사용할 수 있는 독립적인 함수이다.

  • util Function 함수 util Function : 자주 사용되는 기능들을 모듈화하여 재사용할 수 있도록 별도의 파일에 저장해서 사용하는 함수

예 ) useToggle Hook

import { useState } from 'react';

export default function useToggle(initValue = false) {
  const [value, setValue] = useState(initValue);

  const toggleValue = () => {
    setValue(!value);
  };

  return [value, toggleValue];
}

useToggle Hook 은 어떤 요소나 컴포넌트를 토글 시키는 Hook 이다.

그래서 initialValue를 전달받아 해당 요소나 컴포넌트의 렌더 유무를 받을 건데 이 기본값을 false로 저장 했다.

그다음 initialValue를 기본값으로 가지고 있는 value state를 만든다. 그리고 toggleValue 라는 함수로 value의 상태값을 반전시키는 기능을 추가해 토글의 상태인 value와 toggleValue함수를 배열로 return하면 useToggle이라는 Custom Hook 을 만들 수 있다.

이 useToggle Hook을 사용하기 위해 src/component/Faq.js 파일을 만들고 기본 구조를 만든다.

import useToggle from '../hook/useToggle';

export default function Faq() {
  const [isFaqOpen, setIsFapOpen] = useToggle();

  return (
    <>
      <h3>custom hook (useToggle)</h13>
      <div onClick={setIsFapOpen} style={{ cursor: 'pointer' }}>
        Q. 리액트에서 커스텀 훅을 만들 수 있나요?
      </div>
      {isFaqOpen && <div>A., 가능합니다.</div>}
    </>
  );
}

그리고 useToggle Hook에서 배열 형태로 반환했으니 배열로 구조분해해서 isFaqOpen, setIsFaqOpen으로 useState처럼 선언했다. 각자 토글의 상태와 토글의 상태를 반전시키는 함수이다.

마지막으로 div 태그에 onClick 속성으로 setIsFapOpen 함수를 선언하고, isFaqOpen이 true라면 가능하다는 설명이 작성된 div 태그를 렌더해 useToggle Hook을 사용하는 방법이다.


7. 결론

React Hooks는 클래스형 컴포넌트의 복잡한 상태 관리와 라이프사이클 관리 로직을 함수형 컴포넌트로 간결하게 구현할 수 있도록 돕는 강력한 도구이다. 각 Hook의 역할과 특징을 이해하고 상황에 맞게 적절히 사용하는 것이 중요하다.

profile
킵고잉~

0개의 댓글