프론트엔드(Web)/React

리액트 훅 기본 - (useState(),useRef(),useEffect(),useLayoutEffect()

만능 엔터테이너 2024. 8. 27. 17:29
728x90
반응형
SMALL

useState

리액트에서 상태 변수를 선언하고 관리하는 데 사용하는 훅입니다.

가장 기본이면서도 가장 많이 사용하는 중요한 훅입니다.

문법

const [state, setState] = useState(initialState);

기본 사용법

import React, { useState } from 'react';

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

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

😵 꼭 count, set*** 형식의 이름이어야 하나요? 아니에요. 배열의 구조 분해 할당을 떠올려보면 이해하기 쉬워요.

배열의 구조 분해를 떠올려 봐요!

const arr = [10, 20, 30];
const [ a, b, c ] = arr; // a: 10, b:20, c:30
const [ x, y, z ] = arr; // x: 10, y:20, z:30

폼 요소

useState를 사용한 상태 관리 변수를 사용해서 폼 입력 요소의 값을 제어할 수 있습니다.

input[type=”text”]

import { useState } from "react";

const App = () => {
  const [input, setInput] = useState("");
  return (
    <div>
      <input
        type="text"
        value={input}
        onChange={(e) => setInput(e.target.value)}
      />
    </div>
  );
};

export default App;

input[type=’date’]

import { useState } from "react";

const App = () => {
  const [input, setInput] = useState("");
  return (
    <div>
      <input
        type="date"
        value={input}
        onChange={(e) => setInput(e.target.value)}
      />
    </div>
  );
};

export default App;

textarea

import { useState } from "react";

export const App = () => {
  const [textValue, setTextValue] = useState("ab");
  return (
    <>
      <div className="flex flex-col gap-1">
        {textValue}
        <textarea
          className="border border-slate-500"
          value={textValue}
          onChange={(e) => setTextValue(e.target.value)}
        ></textarea>
      </div>
    </>
  );
};

select

import { useState } from "react";

const App = () => {
  const [input, setInput] = useState("banana");
  return (
    <div>
      <select onChange={(e) => setInput(e.target.value)} value={input}>
        <option key={"apple"}>apple</option>
        <option key={"banana"}>banana</option>
        <option key={"orange"}>orange</option>
      </select>
    </div>
  );
};

export default App;

checkbox

import { useState } from "react";

export const App = () => {
  const [checkboxValue, setCheckboxValue] = useState(false);
  const [checkboxValue2, setCheckboxValue2] = useState("");
  return (
    <>
      <div className="flex flex-col gap-1">
        {checkboxValue ? "Agree" : "!Agree"}
        <input
          type="checkbox"
          className="border border-slate-500"
          checked={checkboxValue}
          onChange={() => setCheckboxValue(!checkboxValue)}
        />
        {checkboxValue2}
        <input
          type="checkbox"
          className="border border-slate-500"
          value={checkboxValue2}
          onChange={(e) => setCheckboxValue2(e.target.checked ? "checked" : "")}
        />
      </div>
    </>
  );
};

radio

import { useState } from "react";

export const App = () => {
  const [radioValue, setRadioValue] = useState("male");
  return (
    <>
      <div>
        <input
          type="radio"
          name="gender"
          className="border border-slate-500"
          value={"male"}
          defaultChecked
          onChange={() => setRadioValue("male")}
        />
        male
      </div>
      <div>
        <input
          type="radio"
          name="gender"
          className="border border-slate-500"
          value={"female"}
          onChange={() => setRadioValue("female")}
        />
        female
      </div>
    </>
  );
};

종합

import { useState } from "react";

export const App = () => {
  const [defaultValue, setDefaultValue] = useState("");
  const [textValue, setTextValue] = useState("");
  const [dateValue, setDateValue] = useState("");
  const [checkboxValue, setCheckboxValue] = useState(false);
  const [checkboxValue2, setCheckboxValue2] = useState("");
  const [radioValue, setRadioValue] = useState("male");
  return (
    <>
      <div className="item-middle">
        <div className="grid gap-3">
          <div className="flex flex-col gap-1">
            {defaultValue}
            <input
              type="text"
              className="border border-slate-500"
              value={defaultValue}
              onChange={(e) => setDefaultValue(e.target.value)}
            />
          </div>

          <div className="flex flex-col gap-1">
            {textValue}
            <textarea
              className="border border-slate-500"
              value={textValue}
              onChange={(e) => setTextValue(e.target.value)}
            ></textarea>
          </div>

          <div className="flex flex-col gap-1">
            {dateValue}
            <input
              type="date"
              className="border border-slate-500"
              value={dateValue}
              onChange={(e) => setDateValue(e.target.value)}
            />
          </div>

          <div className="flex flex-col gap-1">
            {checkboxValue ? "Agree" : "!Agree"}
            <input
              type="checkbox"
              className="border border-slate-500"
              checked={checkboxValue}
              onChange={() => setCheckboxValue(!checkboxValue)}
            />
            {checkboxValue2}
            <input
              type="checkbox"
              className="border border-slate-500"
              value={checkboxValue2}
              onChange={(e) =>
                setCheckboxValue2(e.target.checked ? "checked" : "")
              }
            />
          </div>

          <div className="flex flex-col gap-1">
            {radioValue}
            <div>
              <input
                type="radio"
                name="gender"
                className="border border-slate-500"
                value={"male"}
                defaultChecked
                onChange={() => setRadioValue("male")}
              />
              male
            </div>
            <div>
              <input
                type="radio"
                name="gender"
                className="border border-slate-500"
                value={"female"}
                onChange={() => setRadioValue("female")}
              />
              female
            </div>
          </div>
        </div>
      </div>
    </>
  );
};


useRef

useRef는 리액트에서 HTML 요소에 접근하거나 컴포넌트의 렌더링에 영향없이 값을 유지하고 싶을 때 사용합니다.

문법

const ref = useRef(initialValue);

사용 예시

ex1)

import { useState } from "react";
interface Todo {
  id: number;
  text: string;
}
const App = () => {
  let uid = 1;
  const [todos, setTodos] = useState<Todo[]>([]);
  const addTodo = () => {
    const todo = {
      id: uid++,
      text: "아침먹기",
    };
    setTodos((prev) => [...prev, todo]);
  };
  return (
    <div>
      <h1>할일 관리하기</h1>
      <pre>{JSON.stringify(todos)}</pre>
      <button onClick={addTodo}>할일추가</button>
    </div>
  );
};

export default App;

ex2)

import { useRef, useState } from "react";
interface Todo {
  id: number;
  text: string;
}
const App = () => {
  let uid = useRef(1);
  const [todos, setTodos] = useState<Todo[]>([]);
  const addTodo = () => {
    const todo = {
      id: uid.current++,
      text: "아침먹기",
    };
    setTodos((prev) => [...prev, todo]);
  };
  return (
    <div>
      <h1>할일 관리하기</h1>
      <pre>{JSON.stringify(todos)}</pre>
      <button onClick={addTodo}>할일추가</button>
    </div>
  );
};

export default App;

ex3)

import { useRef } from "react";

const App = () => {
  const inputEl = useRef<HTMLInputElement>(null);
  const onButtonClick = () => {
    inputEl.current?.focus();
  };
  return (
    <div>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </div>
  );
};

export default App;

useEffect

useEffect 훅은 컴포넌트 생명 주기에 따른 코드를 작성하고 싶을 때 사용할 수 있는 훅입니다. 아래와 같은 기본 문법 형식을 가집니다.

useEffect(콜백, 의존성배열); // dependencies array
useEffect(() => {}, []);

컴포넌트 생명주기

컴포넌트 생명주기란, 컴포넌트가 생성되고 삭제가 되는 순간까지 거쳐가는 일련의 주기를 말합니다. 리액트에서는 컴포넌트 생명주기를 ‘생성’, ‘수정(업데이트)’, ‘삭제’의 세 가지로 구분하고 있습니다.

컴포넌트 생성

컴포넌트가 생성되는 시점은 특정 컴포넌트가 웹 브라우저에 렌더링 되는 순간입니다.

컴포넌트 삭제

컴포넌트가 삭제되는 시점은 특정 컴포넌트가 웹 브라우저에서 사라지는 순간입니다.

컴포넌트 수정

컴포넌트가 수정되는 시점은 컴포넌트에서 관리하고 있는 상태 관리 변수의 값이 변경되었을 때입니다.

useEffect() 훅 사용하기

컴포넌트 생명주기의 각 시점은 useEffect() 훅으로 체크할 수 있습니다.

컴포넌트 생성 시점

import { useEffect } from "react";

export default function App() {
  useEffect(() => {
    console.log("Component Created");
  }, []);
  return <h1 className="text-3xl font-bold underline">Hello world!</h1>;
}

컴포넌트 수정 시점

import { useEffect, useState } from "react";

export default function App() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    console.log("Component Count Updated!" + count);
  }, [count]);
  return (
    <>
      <h1>{count}</h1>
      <button onClick={() => setCount((count) => count + 1)}>클릭</button>
    </>
  );
}

컴포넌트 삭제 시점

App.tsx

import { useState } from "react";
import Interval from "./components/Interval";

export default function App() {
  const [display, setDisplay] = useState(false);

  return (
    <>
      {display && <Interval />}
      <button onClick={() => setDisplay((display) => !display)}>클릭</button>
    </>
  );
}

Interval.tsx

import { useEffect } from "react";

const Interval = () => {
  useEffect(() => {
    const interval = setInterval(() => {
      console.log("Interval Component Updated!");
    }, 1000);

    return () => {
      clearInterval(interval);
    };
  }, []);

  return (
    <>
      <h1>Interval Component</h1>
    </>
  );
};
export default Interval;

useLayoutEffect

useEffect() 훅과 똑같은 개념이지만, 실행되는 방식에서 차이가 발생합니다.

useEffect() 훅은 화면에 컴포넌트가 렌더링 된 후 비동기적으로 실행됩니다. 그래서 DOM을 조작하는 경우 ‘깜빡임’을 볼 수 있을 확률이 높습니다.

useLayoutEffect()는 화면에 컴포넌트를 그리기 바로 직전에 동기적으로 실행됩니다. 그래서 DOM을 조작하는 경우 ‘깜빡임’을 볼 수 없습니다.

예제 1

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

const UseLayoutEffect = () => {
  const [value, setValue] = useState(0);
  const [value2, setValue2] = useState(0);

  useEffect(() => {
    if (value === 0) {
      setValue(10 + Math.random() * 200);
    }
  }, [value]);

  useLayoutEffect(() => {
    if (value2 === 0) {
      setValue2(10 + Math.random() * 200);
    }
  }, [value2]);

  console.log("render", value);

  return (
    <>
      <button onClick={() => setValue(0)}>useEffect value: {value}</button>
      <br />
      <button onClick={() => setValue2(0)}>
        useLayoutEffect value2: {value2}
      </button>
    </>
  );
};
export default UseLayoutEffect;

예제 2

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

const UseLayoutEffect = () => {
  const [count, setCount] = useState(0);
  const now = performance.now();
  while (performance.now() - now < 200) {
    // Artificial delay -- do nothing
  }
  useLayoutEffect(() => {
    if (count === 10) setCount(0);
    console.log("useLayoutEffect");
  }, [count]);
  return (
    <>
      <h1>Count: {count} </h1>
      <button onClick={() => setCount(10)}>클릭</button>
    </>
  );
};
export default UseLayoutEffect;

 

[스나이퍼 팩토리 리액트 2기 과정]

본 학습 자료 및 코드는 수코딩님의 자료를 이용하였습니다. [수코딩(https://www.sucoding.kr)] 출처

728x90
반응형
LIST