3 분 소요

1. state와 useState()

state란, ‘컴포넌트가 가질 수 있는 상태’

useState는, 컴포넌트의 상태를 간편하게 생성하고 업데이트해주는 도구를 제공




2. useState 사용법

const [state, setState] = useState(초기값);

state의 생성과 동시에 가져야 할 초깃값을 useState 함수에 인자로 넣어주면

state와 setState라는 두 가지 요소를 배열 형태로 리턴해준다.

현재 상태값은 state라는 변수에 들어 있고,

state를 변경시켜주고 싶을 때는 setState라는 함수를 이용해서 변경시켜줄 수 있다.

state와 setState라는 이름은 timesetTime처럼 원하는 대로 바꿔서 사용할 수 있다.

setState 함수를 통해 state를 변경하면 해당 컴포넌트는 화면에 다시 렌더링된다.




3. 예제 1

버튼을 누를 때마다 현재 시각이 1시간씩 올라가도록 만들어보자!

import { useState } from "react";

function App() {
  const [time, setTime] = useState(1);

  const handleClick = () => {
    setTime(time + 1);
  };

  return (
    <div>
      <div>현재 시각: {time}</div>
      <button onClick={handleClick}>Update</button>
    </div>
  );
}

export default App;

Update 버튼을 누를 때마다 컴포넌트가 새롭게 렌더 되면서 현재 시각이 쭉쭉 올라간다.


조금 더 시계답게 만들어보자. 12시보다 숫자가 커지면 다시 1이 되도록 해보자.

import { useState } from "react";

function App() {
  const [time, setTime] = useState(1);

  const handleClick = () => {
    let newTime;
    if (time < 12) newTime = time + 1;
    if (time >= 12) newTime = 1;
    setTime(newTime);
  };

  return (
    <div>
      <div>현재 시각: {time}</div>
      <button onClick={handleClick}>Update</button>
    </div>
  );
}

export default App;




4. 예제 2

이미 만들어놓은 ["김댕댕", "이냥냥"] 배열에 이어서 input 창에 입력하는 내용을 화면에 보여주는 방법이다.

(리액트에서 map을 써서 요소를 출력하려면 key를 꼭 사용해줘야 한다. 배열의 인덱스(idx) 값으로 키를 주도록 하자.)

import { useState } from "react";

function App() {
  const [names, setNames] = useState(["김댕댕", "이냥냥"]);
  const [input, setInput] = useState("");

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

  const handleUpload = () => {
    setNames((prev) => {
      return [input, ...prev];
    });
    setInput("");
  };

  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;

input 태그의 value 속성을 input state로 설정해주고, 사용자가 입력을 할 때마다 핸들링해줄 onChange를 input 태그에 달아주고 handleInputChange 함수를 설정해준다.

입력할 때마다 handleInputChange 함수를 통해 새로 입력된 내용으로 input state의 값을 변경해주고,

Upload 버튼을 누르면 names state의 값인 배열에 새로운 input 값이 추가된다.

우리가 업데이트 시켜줄 스테이트는 이전에 존재하던 스테이트와 밀접한 관련이 있다. 이럴 때는 setNames의 인자에 바로 값을 주는 것이 아니라, 콜백함수를 넣어준다. 리턴하는 값이 새롭게 업데이트될 스테이트!

콜백함수의 인자로는 업데이트 해주기 전 상태의 스테이트를 가지고 있게 된다. 이걸 prevState 혹은 줄여서 prev라고 사용한다.

리턴해줄 값은 새로운 배열이고, 배열에는 input 값과 이전 스테이트가 같이 존재하게 된다.

그리고 추가로 input창 내부를 비워주면 끝!




5. 예제 3

위의 경우, 스테이트가 업데이트 될 때마다(input 창에 한 글자씩 입력될 때마다) 리렌더가 발생한다.

그런데 만약, names state의 초깃값을 가져올 때 연산과 같은 무거운 작업을 해야 한다면 컴포넌트가 매번 렌더되기 때문에 그 무거운 작업이 매번 반복될 것이다. 이것은 성능저하를 초래하게 된다.

import { useState } from "react";

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((prev) => {
      return [input, ...prev];
    });
    setInput("");
  };

  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;

heavyWork라는 엄청 무거운 작업인 함수를 초깃값에서 불러야 하는 경우, 일단 처음 렌더되면 heavyWork 함수가 실행된다.

그리고 스테이트를 업데이트 할 때마다 엄청 무거운 작업이 불리게 된다.


이럴 때 사용하는 방법은!! useState의 초기값에 콜백함수를 넣어주고, 콜백함수의 리턴 값에 무거운 작업인 heavyWork 함수를 호출해주는 것이다.

그러면 처음 렌더 됐을 때만 한 번 실행되고, 그 이후에 스테이트 값이 변경되더라도 더 이상 호출되지 않는다.

import { useState } from "react";

const heavyWork = () => {
  console.log("엄청 무거운 작업!!!");
  return ["김댕댕", "이냥냥"];
};

function App() {
  const [names, setNames] = useState(() => {
    return heavyWork();
  });

  // ... 생략 ...

  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;

📌 별코딩 님의 React Hooks에 취한다 - useState 15분만에 마스터하기를 참고했습니다!

댓글남기기