useState 정리

현수·2023년 6월 5일
0

React Hooks

목록 보기
4/4
post-thumbnail

역할

컴포넌트에서 state 변수를 추가할 수 있는 React Hook 이다.

사용법

useState(initialState)

const [state, setState] = useState(initialState);
  • initialState: 초기 상태값을 지정한다.
    • 함수를 전달하면 초기화할 때 초기화 함수를 호출하고 반환 값을 초기 상태로 저장

useState 는 두개의 값을 가진 배열 반환

  1. initialState 을 통과한 첫번째 상태
  2. 상태를 다른 값으로 업데이트하고 리렌더링을 실행시킬 수 있는 트리거 함수 setState()

setState(nextState)

const [name, setName] = useState('Edward');

function handleClick() {
  setName('Taylor');
  setAge(a => a + 1);
  // ...
  • nextState: 변경할 상태값이다.
    • 함수로 전달하면 이전 상태를 기반으로 상태를 업데이트할 수 있다.

주의사항

  • setState()은 다음 렌더링에서 사용될 상태 변수를 업데이트한다. 고로 상태 변경후 바로 state 를 사용하면 아직 다음 렌더링이 실행되지 않았기 때문에 변경전 state 값을 가지고 있는다.
  • state 값과 이전 state 값이 같을 경우 리렌더링을 건너뛴다.
  • 모든 이벤트 핸들러가 실행되고난후 setState() 함수를 호출해 화면을 업데이트한다. 각 이벤트 핸들러마다 상태를 업데이트할 경우 렌더링이 자주 일어나기 때문이다.

사용예

컴포넌트에 상태 추가

useState 로 초기 상태값과 상태 변경 함수를 얻을 수 있다.

import { useState } from 'react';

function MyComponent() {
  const [age, setAge] = useState(42);
  const [name, setName] = useState('Taylor');
  // ...

setState 으로 상태를 변경하면 새 값으로 컴포넌트를 다시 렌더링하고 UI를 업데이트한다.

function handleClick() {
  setName('Robin');
}

이전 상태를 기반으로 상태 업데이트

여러번 이전 상태를 기반으로 업데이트를 실행할 때 실행중인 코드의 상태 변수는 즉시 업데이트되지 않기 때문에 아래 코드의 각 호출에서 age는 43으로 업데이트된다.

function handleClick() {
  setAge(age + 1); // setAge(42 + 1)
  setAge(age + 1); // setAge(42 + 1)
  setAge(age + 1); // setAge(42 + 1)
}

이때 업데이트 함수를 전달함으로서 해결할 수 있다.

function handleClick() {
  setAge(a => a + 1); // setAge(42 => 43)
  setAge(a => a + 1); // setAge(43 => 44)
  setAge(a => a + 1); // setAge(44 => 45)
}

원리는 다음과 같다. 콜백함수 내의 이전 상태값을 가져와서 다음 상태값을 계산한다. 그리고 변경한 상태값을 임시로 유지하고 있다가(pending) 이후 로직에서 또 업데이트 함수가 있는지 확인하고 있다면 임시 상태값을 기반으로 업데이트 함수를 실행한다. 마지막 업데이트 함수까지 계산했으면 마지막으로 업데이트된 상태값을 최종 상태값으로 저장한다.

객체, 배열 상태의 업데이트

객체와 배열의 상태를 업데이트할때는 기존 객체를 변경하는 대신 교체해야한다.

useState 는 이전 state 값과 현재 state 값이 변경되었는지 확인하고 렌더링 여부를 결정한다. 그러나 객체 내부 값을 변경하는 것은 객체의 참조값을 변경하지 않으므로 값이 변경되었다는 사실을 알리기위해서는 새로운 객체의 참조값으로 교체해야 한다.

// 🚩 다음과 같이 상태를 변경하면 안된다
form.firstName = 'Taylor';
// ✅ 새로운 객체로 교체
setForm({
  ...form,
  firstName: 'Taylor'
});

상태 초기화 함수의 재실행 방지

useState 에서 초기값을 설정은 첫 컴포넌트 렌더링에서만 실행된다. 그러나 초기값 설정에 함수 실행을 전달하면 매 렌더링 마다 함수가 실행된다.

function TodoList() {
  const [todos, setTodos] = useState(createInitialTodos());
  // ...

이 문제를 해결하기 위해 다음과 같이 초기화 함수의 실행 대신 함수의 참조값을 전달해 첫 렌더링에만 함수가 실행되게 할 수 있다.

function TodoList() {
  const [todos, setTodos] = useState(createInitialTodos);
  // ...

key를 이용한 컴포넌트 초기화

컴포넌트로 전달하는 key값을 변경하면 컴포넌트가 초기화된다.

import { useState } from 'react';

export default function App() {
  const [version, setVersion] = useState(0);

  function handleReset() {
    setVersion(version + 1);
  }

  return (
    <>
      <button onClick={handleReset}>Reset</button>
      <Form key={version} />
    </>
  );
}

function Form() {
  const [name, setName] = useState('Taylor');

  return (
    <>
      <input
        value={name}
        onChange={e => setName(e.target.value)}
      />
      <p>Hello, {name}.</p>
    </>
  );
}

이전 렌더링 정보 저장

setState 는 즉시 값이 변경되지 않는다는 점을 이용해서 이전 렌더링 정보를 기반으로 현재 렌더링을 구성할 수 있다. 이 방법은 복잡하지만 useEffect 를 사용하는 것보다 나은 방법이라고 한다.

import { useState } from 'react';

export default function CountLabel({ count }) {
  const [prevCount, setPrevCount] = useState(count);
  const [trend, setTrend] = useState(null);
  console.log(count, prevCount)
  if (prevCount !== count) {
    setPrevCount(count);
    setTrend(count > prevCount ? 'increasing' : 'decreasing');
  }
  return (
    <>
      <h1>{count}</h1>
      {trend && <p>The count is {trend}</p>}
    </>
  );
}

위의 예제는 props 가 이전값과 비교해서 증가했는지 감소했는지를 판별하고싶다.

먼저 CountLabel 은 count 를 props 로 전달 받는다. 이때 count 값이 변경되면 이전 count 값과 다르게되어 조건문이 실행된다.

조건문 내에서 setPrevCount 를 통해 이전 count 값을 업데이트하지만 setState 특성상 현재 코드 흐름에 바로 값이 업데이트되지 않는다.

따라서 setTrend 에서 prevCount 는 아직 이전 렌더링의 count 정보를 가지고 있고 따라서 이전 count 값과 현재 count 값의 비교를 통해 값의 증감을 판별할 수 있다.

레퍼런스

리액트 공식 문서 - useState

0개의 댓글