6 분 소요


1. State 개요

1.1 State 개념

State(상태)란 컴포넌트 내부에서 바뀔 수 있는 값을 의미한다.

React에선 let이나 const가 아닌 State를 사용해서 상태를 표현한다.

왜 변수가 아닌 State를 사용할까? 바로 UI(엘리먼트)를 바꾸기 위해서이다.

전 포스팅에서 name이라는 정보를 const name = "김춘자"; 이라고 만들었는데, 만약 name이라는 값이 바뀌어야만 하는 정보였어야 했다면 state로 생성하는 것이다!


1.2 State 만들기

① State를 만들 때는 useState()를 사용한다.

② React 안에서 특정한 기능을 수행하는 것들을 hook이라고 부른다. (기능 → 훅)

⬇️ useState 훅을 사용하는 방식은 아래와 같다.

// state: 현재 상태의 값이 들어 있는 변수
// setState: 상태를 업데이트하는 함수
//  initialState: 초기 상태 값을 지정
const [state, setState] = useState("initial State");
  • 먼저 const 로 선언을 하고 [ ] 빈 배열 을 생성하고, 배열의 첫번째 자리에는 이 state의 이름, 그리고 두번째 자리에는 set 을 붙이고 state의 이름을 붙힌다.

  • 그리고 useState( ) 의 인자에는 이 state의 원하는 초기값 을 넣어준다.


컴포넌트 내에서 숫자의 증감을 컨트롤할 수 있는 counter이라는 state를 만들어보자

// useState hook
const [count, setCount] = useState[0];



2. State 변경하기

state를 변경할때는 setValue(바꾸고 싶은 값) 를 사용한다.

state란 컴포넌트안에서 변할 수 있는 값 이라는 것을 기억하자!


2.1 useState + onClick Event

Button과 이벤트 핸들러 구현하기

버튼을 클릭했을 때 state가 변경하도록 만들기 위해 <button> 태그를 이용해서 버튼을 생성하자.

// src/App.js

import React from "react";

function App() {
  return (
    <div>
      이름
      <br />
      <button>버튼</button>
    </div>
  );
}

export default App;


이름을 변경할 수 있도록 해야하기 때문에 이름을 state로 선언한다.

// src/App.js
import React, { useState } from "react";

function App() {
  const [name, setName] = useState("박시은");

  return (
    <div>
      <div>{name}</div>
      <button
        onClick={function () {
          setName("시은 천사");
        }}
      >
        버튼
      </button>
    </div>
  );
}
export default App;


그 후, 버튼을 누르면 이름이 변경되도록 해보자.

function App() {
  const [name, setName] = useState("박시은");

  return (
    <div>
      {name}
      <br />
      <button
        onClick={function () {
          setName("시은천사");
        }}
      >
        버튼
      </button>
    </div>
  );
}


아래와 같이 이벤트 핸들러를 사용할 수 있다.

import { useState } from "react";

function App() {
  const [name, setName] = useState("박시은");

  function onClickHandler() {
    setName("시은천사");
  }

  return (
    <div>
      {name}
      <br />
      <button onClick={onClickHandler}>버튼</button>
    </div>
  );
}

export default App;


2.2 useState + onChange Event

아래 그림과 같이 input 태그 안에 값을 입력했을 때 컨텐츠가 변경되도록 해보자.

input에서는 보통 사용자가 입력한 값을 state로 관리하는 패턴을 많이 사용한다.

input태그 안에 value와 onChange()를 넣어주면 된다.

import { useState } from "react";

function App() {
  const [fruit, setFruit] = useState("");

  return (
    <div>
      과일:
      <input
        value={fruit}
        onChange={function (event) {
          setFruit(event.target.value);
        }}
      />
    </div>
  );
}

export default App;

위 코드처럼 이벤트 핸들러 안에서 자바스크립트의 event 객체를 꺼내 사용할 수 있다.

사용자가 입력한 input의 값은 event.target.value 로 꺼내 사용할 수 있다.

정리하자면, 사용자가 input에 어떤 값을 입력하면, 그 값을 입력할 때마다, 같은 말로 onChange될 때마다 value라는 state에 setValue해서 넣어주는 것이다.



3. 실습하기

아이디와 비밀번호에 값을 입력하고 [로그인] 버튼을 누르면 alert로 고객이 입력한 값을 알려주자.

  • 먼저 아래와 같은 화면을 만들어 보자



  • 그후, 다음과 같이 alert의 메세지 내용을 출력하자
    • “고객님이 입력하신 아이디는 ‘입력아이디’ 이며, 비밀번호는 ‘입력비밀번호’ 입니다.”

  • 아래의 사항을 준수하자.
    • (1) id 필드와 비밀번호 필드의 값을 state로 관리하고, 변경이 일어날 때마다 setState를 해서 동기화를 시켜주자.
    • (2) alert로 띄우고 나서는 아이디와 비밀번호 영역을 빈 값으로 초기화 시켜주자.
    • (3) 비밀번호는 보이면 안된다.




먼저 아래와 같이 html을 만들어두자

function App() {
  const [id, setId] = useState("");

  return (
    <div>
      <div>
        아이디 : <input type="text"></input>
      </div>
      <div>
        비밀번호 : <input type="password"></input>
      </div>
      <button>로그인</button>
    </div>
  );
}


id 필드와 비밀번호 필드의 값을 state로 관리하고, 변경이 일어날 때마다 setState를 해서 동기화를 시킨 후 alert로 띄워주자.

import { useState } from "react";

function App() {
  const [id, setId] = useState("");
  const [password, setPassword] = useState("");

  // console.log("id", id);
  // console.log("password", password);

  // id 필드가 변경될 경우
  const onIdChangeHandler = (event) => {
    setId(event.target.value);
  };

  // password 필드가 변경될 경우
  const onPasswordChangeHandler = (event) => {
    setPassword(event.target.value);
  };

  return (
    <div>
      <div>
        아이디 : <input type="text" value={id} onChange={onIdChangeHandler}></input>
      </div>
      <div>
        비밀번호 :{" "}
        <input
          type="password"
          value={password}
          onChange={onPasswordChangeHandler}
        ></input>
      </div>
      <button
        onClick={() => {
          alert(
            `고객님이 입력하신 아이디는 ${id}이며, 비밀번호는 ${password}입니다.`
          );
        }}
      >
        로그인
      </button>
    </div>
  );
}

export default App;


아이디와 비밀번호 영역을 빈 값으로 초기화 시켜주자.

아래와 같이 state 값을 초기화 시켜주면 된다.

setId("");
setPassword("");
<button
  onClick={() => {
    alert(
      `고객님이 입력하신 아이디는 ${id}이며, 비밀번호는 ${password}입니다.`
    );
    setId("");
    setPassword("");
  }}
>
  로그인
</button>



4. 함수형 업데이트 개요

4.1 함수형 업데이트 사용법

함수형 업데이트 방식을 통해 setState를 사용할 수 있다.

// 일반 사용법(기존 방식)
setState(number + 1);

// 함수형 업데이트
setState(() => {});

① setState의 ( ) 안에 수정할 값이 아니라, 함수를 넣을 수 있다.
② 그 함수의 인자에서는 현재의 state을 가져올 수 있고, { } 안에서는 이 값을 변경하는 코드를 작성할 수 있다.


// 현재 number의 값을 가져와서 그 값에 +1을 더하여 반환
setState((currentNumber) => {
  return currentNumber + 1;
});


4.2 함수형 업데이트 필요성(with 배치)

일반 사용법과 함수형 업데이트 방식의 차이점

일반 업데이트 방식으로 onClick안에서 setNumber(number + 1) 를 3번 호출 → number가 1씩 증가

// src/App.js

import { useState } from "react";

const App = () => {
  const [number, setNumber] = useState(0);
  return (
    <div>
      {/* 버튼을 누르면 1씩 플러스된다. */}
      <div>{number}</div>
      <button
        onClick={() => {
          setNumber(number + 1); // 첫번째 줄
          setNumber(number + 1); // 두번쨰 줄
          setNumber(number + 1); // 세번째 줄
        }}
      >
        버튼
      </button>
    </div>
  );
};

export default App;

① 일반 업데이트 방식은 버튼을 클릭했을 때 첫번째 줄 ~ 세번째 줄의 있는 setNumber가 각각 실행되는 것이 아니라, 배치(batch)로 처리⭐한다.

② 즉 onClick을 했을 때 setNumber 라는 명령을 몇 번을 내리던지 간에, 리액트는 그 명령을 하나로 모아 최종적으로 한번만 실행시킨다.

③ 따라서 setNumber을 3번 명령하던, 100번 명령하던 1번만 실행된다.


함수 업데이트 방식으로 onClick안에서 setNumber(number + 1) 를 3번 호출 → number가 3씩 증가

// src/App.js

import { useState } from "react";

const App = () => {
  const [number, setNumber] = useState(0);
  return (
    <div>
      {/* 버튼을 누르면 3씩 플러스 된다. */}
      <div>{number}</div>
      <button
        onClick={() => {
          setNumber((previousState) => previousState + 1);
          setNumber((previousState) => previousState + 1);
          setNumber((previousState) => previousState + 1);
        }}
      >
        버튼
      </button>
    </div>
  );
};

export default App;

① 함수형 업데이트 방식은 3번을 동시에 명령을 내리면, 그 명령을 모아 순차적으로 각각 1번씩 실행시킨다.

② 0에 1더하고, 그 다음 1에 1을 더하고, 2에 1을 더해서 3을 출력한다.

③ 이는 불필요한 리-렌더링을 방지(렌더링 최적화)하여 리액트의 성능을 향상시키기 위해 단일 업데이트(batch update)로 한꺼번에 처리할 수 있게 하는 것이다.


4.2 함수형 업데이트의 문제점

리액트에서 상태 업데이트는 비동기적으로 이루어지기 때문에 상태가 즉시 업데이트 되지 않는다.

따라서 아래와 같은 문제가 발생하게 된다.

import { useState } from "react";

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

  function handleClick() {
    // 첫 번째 상태 업데이트
    setCount(count + 1);
    console.log(count); // 아직 상태 변경 x, 초기 상태가 0이므로 0 출력 (비동기적으로 작동하기 때문)

    // 두 번째 상태 업데이트
    setCount(count + 1);
    console.log(count); // 아직 상태 변경 x, 이전 상태의 값 1 출력 (비동기적으로 작동하기 때문)
  }

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={handleClick}>Increase</button>
    </div>
  );
}

export default App;

setCount 호출에서는 각각 현재 상태가 아닌 이전 상태를 기반으로 하고 있기 때문에, 예상대로 두 번의 클릭에도 불구하고 상태가 1만 증가하게된다.


4.3 함수형 업데이트 문제 해결방안

“useEffect + 함수형 업데이트 사용”을 하면 함수형 업데이트 문제(배치 업데이트로 업데이트 값이 즉시 반영되지 않는 문제)를 해결할 수 있다.

import { useEffect, useState } from "react";

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

  useEffect(() => {
    console.log("useEffect");
    console.log(count);
  }, [count]);

  function handleClick() {
    // 첫 번째 상태 업데이트
    setCount((prev) => prev + 1);
    console.log(count); // 아직 상태 변경 x (비동기적으로 작동하기 때문)

    // 두 번째 상태 업데이트
    setCount((prev) => prev + 1);
    console.log(count); // 아직 상태 변경 x (비동기적으로 작동하기 때문)
  }

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={handleClick}>Increase</button>
    </div>
  );
}

export default App;

useEffect에 대해선 다음 포스팅에서 자세히 다룰 것이다!


태그:

카테고리:

업데이트:

댓글남기기