본문 바로가기
React-study/dil

[DIL] useState 사용법

by 어느새벽 2024. 5. 20.

컴포넌트에 state 추가하기 

컴포넌트의 최상위 레벨에서 useState를 호출하여 하나 이상의 state 변수를 선언한다.

import { useState } from 'react';

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

배열 구조 분해를 사용하여 [something, setSomething]과 같은 state 변수의 이름을 지정하는 것이 관례이다.

 

useState는 state 변수는 처음에 제공한 초기 state이다.

그리고 상호작용에 반응하여 다른 값으로 변경할 수 있는 set함수이다.

화면의 내용을 업데이트하려면 다음 state로 set 함수를 호출해야 한다.

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

React는 다음 state를 저장하고 새로운 값으로 컴포넌트를 다시 렌더링한 후 UI를 업데이트합니다.

 

**주의사항

set 함수를 호출해도 이미 실행 중인 코드의 현재 state는 변경되지 않는다.

function handleClick() {
  setName('Robin');
  console.log(name); // Still "Taylor"!
                     // 아직 "Taylor"입니다!
}

set함수는 다음 렌더링에서 반환할 useState에만 영향을 준다.

 

useState 기본 예시

1. 카운터 (숫자)

예제에서 count state 변수는 숫자를 받고 버튼을 클릭하면 숫자가 증가한다.

import { useState } from 'react';

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

  function handleClick() {
    setCount(count + 1);
  }

  return (
    <button onClick={handleClick}>
      You pressed me {count} times
    </button>
  );
}

 

2. 텍스트필드 (문자열)

예제에서 text state 변수는 문자열을 받습니다. input에 타이핑하면 handleChange는 input DOM 요소에서 최신 input 값을 읽고 setText를 호출하여 state를 업데이트합니다. 이렇게 하면 아래에 현재 text를 표시할 수 있다.

import { useState } from 'react';

export default function MyInput() {
  const [text, setText] = useState('hello');

  function handleChange(e) {
    setText(e.target.value);
  }

  return (
    <>
      <input value={text} onChange={handleChange} />
      <p>You typed: {text}</p>
      <button onClick={() => setText('hello')}>
        Reset
      </button>
    </>
  );
}

 

3.체크박스 (불리언)

예제에서 liked state 변수는 불리언을 받는다. input을 클릭하면 setLiked는 체크박스가 선택되어 있는지 여부에 따라 liked state 변수를 업데이트한다. liked 변수는 체크박스 아래의 텍스트를 렌더링하는 데 사용된다.

import { useState } from 'react';

export default function MyCheckbox() {
  const [liked, setLiked] = useState(true);

  function handleChange(e) {
    setLiked(e.target.checked);
  }

  return (
    <>
      <label>
        <input
          type="checkbox"
          checked={liked}
          onChange={handleChange}
        />
        I liked this
      </label>
      <p>You {liked ? 'liked' : 'did not like'} this.</p>
    </>
  );
}

 

4. 폼(두 개의 변수)

동일한 컴포넌트에 두 개 이상의 state 변수를 선언할 수 있다. 각 state 변수는 완전히 독립적이다.

import { useState } from 'react';

export default function Form() {
  const [name, setName] = useState('Taylor');
  const [age, setAge] = useState(42);

  return (
    <>
      <input
        value={name}
        onChange={e => setName(e.target.value)}
      />
      <button onClick={() => setAge(age + 1)}>
        Increment age
      </button>
      <p>Hello, {name}. You are {age}.</p>
    </>
  );
}

 

 

이전 state를 기반으로 state 업데이트하기 

age42라고 가정하면 이 핸들러는 setAge(age + 1)를 세 번 호출한다.

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

 

그러나 클릭해보면 age는 45가 아닌 43이 된다. set함수를 호출해도 이미 실행 중인 코드에서 age state 변수는 업데이트 되지 않는다. 따라서 43이된다.

이 문제를 해결하려면 state 대신 setAge에 업데이터 함수를 전달 해야 한다.

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

여기서 a => a+1은 업데이터 함수이다. 이 함수는 대기 중인 state를 가져와서 다음 state를 계산한다.

React는 업데이터 함수를 에 넣는다. 그러면 다음 렌더링 중에 동일한 순서로 호출한다.

a => a + 1은 대기 중인 state로 42를 받고 다음 state로 43을 반환한다.

a => a + 1은 대기 중인 state로 43을 받고 다음 state로 44를 반환한다.

a => a + 1은 대기 중인 state로 44를 받고 다음 state로 45를 반환한다다.

대기 중인 다른 업데이트가 없으므로, React는 결국 45를 현재 state로 저장한다다.

관례상 대기 중인 state 인수의 이름을 agea와 같이 state 변수 이름의 첫 글자로 지정하는 것이 일반적이나 prevAge 또는 더 명확하다고 생각하는 다른 이름으로 지정해도 된다.

React는 개발 환경에서 순수한지 확인하기 위해 업데이터를 두 번 호출할 수 있다.

 

객체 및 배열 state 업데이트 

state에는 객체와 배열도 넣을 수 있다. React에서 state는 읽기 전용으로 간주되므로 기존 객체를 변이하지 않고, 교체를 해야 한다. 예를 들어, state에 form 객체가 있는 경우 변이하면 안된다.

// 🚩 state 안에 있는 객체를 다음과 같이 변이하지 마세요: 
form.firstName = 'Taylor';

// ✅ 새로운 객체로 state 교체합니다
setForm({
  ...form,
  firstName: 'Taylor'
});

 

초기 state 다시 생성하지 않기 

React는 초기 state를 한 번 저장하고 다음 렌더링부터는 이를 무시한다.

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

createInitialTodos()의 결과는 초기 렌더링에만 사용되지만, 여전히 모든 렌더링에서 이 함수를 호출하게 된다. 이는 큰 배열을 생성하거나 값비싼 계산을 수행하는 경우 낭비가 될 수 있다.

이 문제를 해결하려면, 대신 이를 useState초기화 함수로 전달한다.

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

함수를 호출한 결과인 createInitialTodos()가 아니라 함수 자체인 createInitialTodos를 전달하고 있다는 것에 주목해라. 함수를 useState에 전달하면 React는 초기화 중에만 함수를 호출한다.

개발 환경에서는 React가 초기화 함수가 순수한지 확인하기 위해 초기화 함수를 두 번 호출할 수 있다.

 

key로 state 재설정하기 

목록을 렌더링할 때 key 속성을 자주 접하게 된다. 하지만 key 속성은 다른 용도로도 사용된다.

컴포넌트에 다른 key를 전달하여 컴포넌트의 state를 재설정할 수 있다. 이 예제에서는 Reset 버튼이 version state 변수를 변경하고, 이를 Formkey로 전달한다. key가 변경되면 React는 Form 컴포넌트(및 그 모든 자식)를 처음부터 다시 생성하므로 state가 초기화된다.

 

이전 렌더링에서 얻은 정보 저장하기 

보통은 이벤트 핸들러에서 state를 업데이트한다. 하지만 드물게 렌더링에 대한 응답으로 state를 조정해야 하는 경우도 있다. 예를 들어, props가 변경될 때 state 변수를 변경하고 싶을 수 있다. 대부분의 경우 이 기능은 필요없다.

필요한 값을 현재 props나 다른 state에서 모두 계산할 수 있는 경우, 중복되는 state를 모두 제거해야한다. 너무 자주 재계산하는 것이 걱정된다면, useMemo 훅을 사용하면 도움이 될 수 있다.

전체 컴포넌트 트리의 state를 재설정하려면 컴포넌트에 다른 key를 전달해야 한다.

가능하다면 이벤트 핸들러의 모든 관련 state를 업데이트한다.

이 중 어느 것에도 해당하지 않는 희귀한 경우에는, 컴포넌트가 렌더링되는 동안 set 함수를 호출하여 지금까지 렌더링된 값을 기반으로 state를 업데이트하는 데 사용할 수 있는 패턴이 있다.

다음은 그 예시이다. CountLabel 컴포넌트는 전달된 count props를 표시한다:

export default function CountLabel({ count }) {
  return <h1>{count}</h1>
}

 

카운터가 마지막 변경 이후 증가 또는 감소했는지를 표시하고 싶다고 가정하면 count prop은 이를 알려주지 않으므로 이전 값을 추적해야 한다. 이를 추적하기 위해 prevCount state 변수를 추가한다. trend라는 또 다른 state 변수를 추가하여 count의 증가 또는 감소 여부를 추적해라. prevCountcount를 비교해서, 같지 않은 경우 prevCounttrend를 모두 업데이트한다. 이제 현재 count props와 마지막 렌더링 이후 count가 어떻게 변경되었는지 모두 표시할 수 있다.

 

'React-study > dil' 카테고리의 다른 글

[DIL] useContext  (0) 2024.05.29
[DIL] useReducer(2)  (0) 2024.05.28
[DIL] useReducer(1)  (0) 2024.05.27
[DIL] useState(2)  (0) 2024.05.17
[DIL] useState(1)  (0) 2024.05.16