[React] Basic Hook (1) _ useState

박하늘·2025년 2월 15일

React

목록 보기
3/15
post-thumbnail

useState란 ?

  • useState는 React의 훅(Hook) 중 하나로, 컴포넌트에서 상태(state)를 관리할 때 사용
  • 컴포넌트가 렌더링될 때마다 상태를 유지할 수 있도록 도와줍니다.
  • 동일한 컴포넌트가 여러개라도 state는 각각 관리된다.

기본 사용 방법 및 구조

📍 기본 문법

const [state, setState] = useState(initialValue);
  • state → 현재 상태 값 (상태 값)
  • setState → 상태를 변경하는 함수 (상태 변경 함수)
  • initialValue → 초기 상태 값 (숫자, 문자열, 배열, 객체 등 가능)



1️⃣ 초기 상태 값 _ 숫자

📍 예제

import { useState } from "react";

function Counter() {
  const [count, setCount] = useState(0); // 초기값 0 설정

  return (
    <div>
      <p>현재 카운트: {count}</p>
      <button onClick={() => setCount(count + 1)}>+1</button>
    </div>
  );
}

🔮 초기 상태 (0) 을 버튼 클릭 시 1씩 증가된 값으로 바꿔주는 App

  • import { useState } from "react"; 를 통하여 import 해야 함
  • useState(0) → 초기 값을 0으로 설정
  • onClick={() => setCount(count + 1) 버튼에 onClick 이벤트를 사용하여 클릭 시 count 숫자가 1씩 증가
    이때, 상태 값(count)이 아닌 상태 변경 함수(setCount)를 이용하여 값을 변경 해주어야 한다 !



2️⃣ 초기 상태 값 _ 문자열

📍 예제

import { useState } from 'react'
import './App.css'

function App() {
  const [text, setText] = useState("안녕하세요!");
  const [inputValue, setInputValue] = useState("")

  const handleClick = () => {
    setText(inputValue);
  };

  return (
    <div>
      <h2>{text}</h2>
      <input type={text} value={inputValue} onChange={(e) =>
        setInputValue(e.target.value)}></input>
      <br/>
      <button onClick={handleClick}>☄️</button>
    </div>
  );
}

export default App

🔮 초기 상태 ('안녕하세요!') 를 버튼 클릭 시 input 값에 있는 값으로 바꿔주는 App

  • text의 상태 변경 함수(setText) 값을 inputValue의 값으로 바꿔주는 handleClick() 함수 생성
  • value={inputValue} : input 값에 들어오는 value 값을 inputValue 상태 값으로 설정
  • onChange={(e) => setInputValue(e.target.value)} : input 값이 변경(change) 되면 이벤트 실행
    setInputValue 상태 변경 함수에 해당 value 값으로 설정
  • onClick={handleClick} : 버튼 클릭 시 handleClick() 함수 실행


3️⃣ 초기 상태 값 _ 배열

📍 예제

import { useState } from 'react'
import './App.css'

function App() {
  const [todos, setTodos] = useState(["할 일 1", "할 일 2"]);
  const [inputValue, setInputValue] = useState("");

  const addTodo = () => {
  	if (inputValue.trim() === "") return; // 빈 문자열 방지
    setTodos([...todos, `${inputValue}`]);
    setInputValue(""); // 빈 공백으로 초기화
  };

  return (
    <>
    <div>
      <ul>
        {todos.map((todo, index) => (
          <li key={index}>{todo}</li>
        ))}
      </ul>
      <input type='text' value={inputValue}
      onChange={(e) => setInputValue(e.target.value)}></input>
      <button onClick={addTodo}>할 일 추가</button>
    </div>
    </>
  );
}

export default App

🔮 초기 상태의 할 일 목록에서 버튼 클릭 시 input 값에 있는 값이 할 일 목록에 추가되는 App

  • useState(["할 일 1", "할 일 2"]) → 배열 상태 초기화
  • setTodos([...todos, `${inputValue}`]) → 기존 배열을 복사하고 새로운 값(inputValue) 추가
  • {todos.map((todo, index) => (<li key={index}>{todo}</li>))
    todos를 순회하여 새로운 배열(map)을 만든다.
    그 안에 인자는 todo 와 index 가 있으며, key 값은 index로 주고 <li> 에 들어가는 텍스트는 todo로 넣어준다.


4️⃣ 초기 상태 값 _ 객체

📍 예제

import { useState } from 'react'
import './App.css'

function App() {
  const [user, setUser] = useState({ name: "홍길동", age: 20 });

  return (
    <div>
      <p>이름: {user.name}</p>
      <p>나이: {user.age}</p>
      <button onClick={() => setUser({ ...user, age: user.age + 1 })}>
        나이 증가
      </button>
    </div>
  );
}

export default App

🔮 초기 상태의 객체 name: 홍길동, age: 20에서 버튼 클릭 시 나이만 증가하는 App

  • useState({ name: "홍길동", age: 25 }) → 객체 상태 초기화
  • setUser({ ...user, age: user.age + 1 }) → 기존 객체를 유지하면서 나이만 변경



5️⃣ 초기 상태 값 _ 콜백 함수(Callback)✨

🔮 [예제_설명]
input에 값을 넣고 추가 버튼 클릭 시 목록 순서대로 추가가 되는데,
이때 추가하는 작업이 들어있는 함수인 heavyWork()에서 무거운 작업이 이루어 질 때 ...

📍 잘못된 예제 (❌)

import { useState } from 'react'
import './App.css'

const heavyWork = () =>{
  console.log('엄청 무거운 작업!!!') ;
  return ['홍길동','김민수'];
}

function App() {
  const [names, setNames] = useState(heavyWork())
  const [input, setInput] = useState("");

  const handleInputChange = (e) => {
  setInput (e.target.value);
  };

  const handleUpload = () => {
  setNames((prevState) => {
    console.log('이전 state: ', prevState);
    return[input, ...prevState]
  })
}

  return ( 
  <div>
    <input type="text" value={input} onChange={handleInputChange}/>
    <button onClick={handleUpload}>Upload</button>
    {names.map( (name, idx) => {
    return <p key={idx}>{name}</p>;
    })}
    </div>
    ) ;
    }
  
export default App
  • const [names, setNames] = useState(heavyWork()) : 입력 시 해당 작업이 첫 랜더링, 입력 변경 시 모두 리턴됨
    → 성능이 저하되며 로딩이 느려질 수 있음

📍 잘못된 예제 수정 코드 (⭕️)

function App() {
  const [names, setNames] = useState(() => {
  return heavyWork();
})
  • 위처럼 Callback 함수를 통하여 return 값에 넣어주면 첫 렌더링시에만 작업이 진행된다


🔖 추가 예제 (1)

import { useState } from 'react'
import './App.css'

function App() {
  return (
    <>
    <Hello />
    <Hello />
    </>
  )
}

function Hello (){
  const [name, setName] = useState('neul')

return (
  <>
  <h1>이름 변경 [useState]</h1>
  <h2>{name}</h2>
  <button onClick={()=> setName(name === 'neul' ? 'reum' : 'neul')}>🔀</button>
    
  </>
)

}

export default App

🔽 [실행 화면] 🔽

🔮 한 컴포넌트가 여러개 일 때

  • 동일한 컴포넌트여도 state는 개별적으로 관리된다.
  • <Hello /> 라는 컴포넌트가 여러 개로 이름 바꿔주는 버튼이 여러 개 일 때, 동일하게 변동되지 않고, 각 버튼마다 개별적으로 관리된다.
  • [ 👀 ] 상자 버튼만 누르면 해당 state 만 변경 된다 !



🚨 사용 주의사항

⚡️ 상태를 직접 수정하면 안 됨! ( 상태 변경 함수 - setState 사용 필수)

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

count = count + 1; // ❌ 잘못된 방식
setCount(count + 1); // ✅ 올바른 방식

⚡️ 비동기적으로 동작함 (업데이트가 즉시 반영되지 않음)

setCount(count + 1);
console.log(count); // 이전 값이 출력될 수도 있음

⚡️ 이전 상태를 기반으로 업데이트할 때 최신 상태를 저장해주는 prevState 사용

setCount(prev => prev + 1);

→ 이렇게 하면 count 값이 정확하게 업데이트됨.

🍒 마무리 🍒

원래 React에서 상태를 관리하려면 클래스 컴포넌트를 사용해야 했지만, React 16.8부터 함수형 컴포넌트에서도 상태를 관리할 수 있도록 훅이 도입되었다.

class형 함수를 이용하여 상태를 관리하려면 코드가 길어져 어렵고 복잡했지만 함수형 컴포넌트와 훅을 이용하여 상태를 관리 할 수 있다는 점이 최고의 장점인 것 같다.

이 점을 잘 이용하여 더 간결하고 쉽고 좋은 코드 개발을 할 것이다.

0개의 댓글