Notice
Recent Posts
Recent Comments
Link
«   2025/10   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

죽이죽이

React : Built-in Hooks 본문

React

React : Built-in Hooks

죽이죽이 2024. 2. 12. 12:12

빌트인 훅이란?

※ 정의
- 빌트인 훅은 함수형 컴포넌트에서 클래스형 컴포넌트의 상태 및 생명주기와 같은 기능을 다룰 수 있도록 도와주는 특별한 함수들을 말한다.
- React 16.8 버전에서 도입되었다.

 

 

 

useState

※ 사용하는 이유
- 특정 컴포넌트 안에서 상태값을 관리할 때 사용한다.



※ 주의할 점
- props, state의 변경과 무관하게 부모컴포넌트가 리렌더링 될 경우 자식 컴포넌트도 무조건 리렌더링이 된다.

 

 

 

useRef

※ 사용하는 이유
- DOM에 직접 접근해서 특정 값을 가져오는 등의 작업이 필요할 때 사용된다. (요소의 크기, 스크롤바 위치, 포커스 등)



※ 주의할 점
- useRef를 사용하면 참조하는 값이 변경되더라도 컴포넌트의 리랜더링이 발생하지 않는다.



※ 예시

import React, { useRef, useState } from "react";

export default function Ref() {
  const [input, setInput] = useState("");
  const ref = useRef(null);

  const handleSubmit = (e) => {
    e.preventDefault();

    if (!input.trim()) {
      // 해당 요소에 focus
      ref.current.focus();
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input type="text" value={input} onChange={(e) => setInput(e.target.value)} ref={ref} />
      <button>Submit</button>
    </form>
  );
}

 

 

 

 

forwardRef, useImperativeHandle

사용하는 이유
- forwardRef :
부모 컴포넌트에서 선언한 ref를 자식 컴포넌트에 전달하기 위해 사용한다.

- useImperativeHandle : 인자로 받은 부모의 ref를 자식 컴포넌트 측에서 커스터마이징할 수 있다.

 


※ 예시

import React, { forwardRef, useImperativeHandle, useRef, useEffect } from 'react';

// 자식 컴포넌트
const ChildComponent = forwardRef((props, ref) => {
  const inputRef = useRef();

  // useImperativeHandle을 사용하여 ref로 전달할 함수나 속성을 정의
  useImperativeHandle(ref, () => ({
    // 외부에서 호출 가능한 focus 함수를 정의
    focus: () => {
      inputRef.current.focus();
    },
  }));

  return <input ref={inputRef} placeholder="Type here" />;
});


// 부모 컴포넌트
const ParentComponent = () => {
  const childRef = useRef();

  useEffect(() => {
    // 부모 컴포넌트에서 자식 컴포넌트의 focus 함수 호출
    childRef.current.focus();
  }, []);

  return (
    <div>
      <p>Clicking the button will focus the input in the child component:</p>
      <ChildComponent ref={childRef} />
    </div>
  );
};

export default ParentComponent;

 

 

 

 

useEffect

※ 사용하는 이유
- 함수형 컴포넌트에서 side effects를 처리하기 위한 Hook으로 컴포넌트의 마운트, 업데이트, 언마운트 시 수행할 작업들을 지정할 때 사용한다.
- 의존성 배열을 통해 특정 값이 변할 때 실행을 해줄 수 있다.
- return문을 사용해 컴포넌트가 언마운트되거나 의존성 배열의 값이 변경될 때 clean-up을 해줄 수 있다.
- 주로 API 요청을 통해 데이터를 가져올 때 많이 사용한다.


※ 예시
- 소스 코드

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

export default function Effect() {
  const [firstFlag, setFirstFlag] = useState(false);
  const [secondFlag, setSecondFlag] = useState(false);

  useEffect(() => {
    console.log("의존성 배열 X => 컴포넌트 렌더링 시마다 호출");
  });

  useEffect(() => {
    console.log("의존성 배열 (빈배열) => 최초 렌더링(마운트) 시에만 호출");
  }, []);

  useEffect(() => {
    console.log("의존성 배열 (값) => 값이 바뀔때마다 호출");
  }, [firstFlag]);

  const handleFlagChange = () => {
    console.log("값 변경 O");
    setFirstFlag((prev) => !prev);
  };

  const handleFlagNotChange = () => {
    console.log("값 변경 X");
    setSecondFlag((prev) => !prev);
  };

  return (
    <>
      <button onClick={handleFlagChange}>First Flag State Change</button>
      <button onClick={handleFlagNotChange}>Second Flag State Change</button>
    </>
  );
}

- 화면

- 컴포넌트 최초 마운트

- First Flag State Change 버튼 클릭

- Second Flag State Change 버튼 클릭

 

 

 

useReducer

※ 사용하는 이유
- 가독성 : 상태 변경 로직이 복잡해질 경우 useReducer를 사용하여 상태변경 로직을 컴포넌트와 분리시켜 별도로 관리할 수 있다.
- 재사용성 : 공통적으로 사용하는 상태관련 로직이 있을 경우 useReducer를 사용하여 코드를 줄일 수 있다.


※ 예시
- Reducer.jsx

import React, { useReducer, useState } from "react";
import signupReducer from "../../reducer/signupReducer";

export default function Reducer() {
  // 기존 방식
  // const [id, setId] = useState("");
  // const [password, setPassword] = useState("");
  // const [name, setName] = useState("");

  // Reducer 사용
  const [signupState, dispatchSignup] = useReducer(signupReducer, {
    id: "",
    password: "",
    name: "",
  });

  console.log(signupState);

  const handleId = (e) => {
    // setId(e.target.value);
    dispatchSignup({ type: "id", value: e.target.value });
  };

  const handlePassword = (e) => {
    // setPassword(e.target.value);
    dispatchSignup({ type: "password", value: e.target.value });
  };

  const handleName = (e) => {
    // setName(e.target.value);
    dispatchSignup({ type: "name", value: e.target.value });
  };

  return (
    <div>
      <div>
        <label htmlFor="">아이디 : </label>
        <input type="text" value={signupState.id} onChange={handleId} />
      </div>
      <div>
        <label htmlFor="">비밀번호 : </label>
        <input type="password" value={signupState.password} onChange={handlePassword} />
      </div>
      <div>
        <label htmlFor="">이름 : </label>
        <input type="text" value={signupState.name} onChange={handleName} />
      </div>
    </div>
  );
}​

- signupReducer.js
const signupReducer = (state, action) => {
  // 상태 관련 로직을 분리할 수 있다.
  switch (action.type) {
    case "id":
      return {
        ...state,
        id: action.value,
      };
    case "password":
      return {
        ...state,
        password: action.value,
      };
    case "name":
      return {
        ...state,
        name: action.value,
      };
  }
};

export default signupReducer;​

 

 

 

 

useContext

※ 사용하는 이유
- 전역적으로 특정 상태를 관리해야할 경우 사용한다.
- 상태 공유가 필요하지만 컴포넌트간 거리가 많이 떨어져있을 경우 사용한다.


※ 주의할 점
- 모든 상태를 context로 관리하는것은 좋지 않다.

- 잦은 상태변화가 있을 경우에는 context를 사용하는 것을 지양하는 것이 좋다.


※ 예시
- App.js
import { useState } from 'react';
import './App.css';
import Header from './components/Header/Header';
import TodoList from './components/TodoList/TodoList';
import { DarkModeProvider } from './context/DarkModeContext';

const filters = ['all', 'active', 'completed'];
function App() {
  const [filter, setFilter] = useState(filters[0]);
  return (
    <DarkModeProvider>
      <Header filters={filters} filter={filter} onFilterChange={setFilter} />
      <TodoList filter={filter} />
    </DarkModeProvider>
  );
}

export default App;

- DarkModeContext.jsx
import { createContext, useContext, useEffect, useState } from "react";

const DarkModeContext = createContext();

export function DarkModeProvider({ children }) {
  const [darkMode, setDarkMode] = useState(false);
  const toggleDarkMode = () => {
    setDarkMode(!darkMode);
    updateDarkMode(!darkMode);
  };

  useEffect(() => {
    const isDark = localStorage.theme === "dark" ? true : false;
    setDarkMode(isDark);
    updateDarkMode(isDark);
  }, []);

  return (
    <DarkModeContext.Provider value={{ darkMode, toggleDarkMode }}>
      {children}
    </DarkModeContext.Provider>
  );
}

function updateDarkMode(darkMode) {
  if (darkMode) {
    document.documentElement.classList.add("dark");
    localStorage.theme = "dark";
  } else {
    document.documentElement.classList.remove("dark");
    localStorage.theme = "light";
  }
}
export const useDarkMode = () => useContext(DarkModeContext);

- Header.jsx
import React from 'react';
import { useDarkMode } from '../../context/DarkModeContext';
import styles from './Header.module.css';
import { HiMoon, HiSun } from 'react-icons/hi';

export default function Header({ filters, filter, onFilterChange }) {
  // darkModeContext 사용
  const { darkMode, toggleDarkMode } = useDarkMode();

  return (
    <header className={styles.header}>
      <button onClick={toggleDarkMode} className={styles.toggle}>
        {!darkMode && <HiMoon />}
        {darkMode && <HiSun />}
      </button>
      <ul className={styles.filters}>
        {filters.map((value, index) => (
          <li key={index}>
            <button
              className={`${styles.filter} ${
                filter === value && styles.selected
              }`}
              onClick={() => onFilterChange(value)}
            >
              {value}
            </button>
          </li>
        ))}
      </ul>
    </header>
  );
}

 

'React' 카테고리의 다른 글

React : 이미지 처리  (0) 2024.02.13
React : Form 데이터 조작 방법  (0) 2024.02.12
React : React 프로젝트 폴더 구성  (0) 2024.02.09
React : React를 사용하는 이유  (0) 2024.02.09
React : 프로젝트 생성하기  (0) 2024.01.25