⚙️ 공식문서와 useState 알아보기

박원빈·2024년 5월 16일

리액트 ⚙️

목록 보기
5/14
post-thumbnail

State: 컴포넌트의 기억 저장소

컴포넌트는 상호 작용의 결과로 화면의 내용을 변경해야되는 경우가 많습니다.
(Ex. 폼에 입력하면 입력 필드가 업데이트 되어야하고, 캐러셀에서 다음을 클릭할 때 표시되는 내용이 변경되어야 하고, 구매를 클릭하였을 때 상품이 장바구니에 담겨야 함.)
컴포넌트는 현재 입력값과 현재 이미지, 장바구니와 같은 것들을 "기억"해야 합니다.
React는 이런 종류의 컴포넌트별 메모리를 state라고 부릅니다.

유저가 상호작용을 하면 그에 맞게 UI가 변경되어야하는 경우에
컴포넌트는 그 상호작용에 맞는 것들을 "기억"해서 보여주어야 하는 경우가 생깁니다.
React에서는 그 데이터들을 state라고 부릅니다.

useState(initialState)

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

import { useState } from 'react';

function MyComponent() {
  const [age, setAge] = useState(28);
  const [name, setName] = useState('Taylor');
  const [todos, setTodos] = useState(() => createTodos());
  // ...

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

매개변수

  • inititalState : state의 초기 설정값이며, 어떤 유형의 값이든 지정할 수 있지만 함수에 대해서는 특별한 동작이 있습니다. 이 인수는 초기 렌더링(mount) 이후에는 무시됩니다.
    • 함수를 initialState로 전달하면 이를 초기화 함수로 취습합니다.
      이 함수는 순수해야 하고 인수를 받지 않아야 하며 반드시 어떤 값을 반환해야 합니다.
      React는 컴포넌트를 초기화할 때 초기화 함수를 호출하고, 그 반환값을 초기 state로 저장합니다.

반환값

useState는 정확히 두 개의 값을 가진 배열을 반환합니다.

const [state, setState] = useState("initialState");
  1. 현재 state입니다. 첫 번째 렌더링 중에는 전달한 initialState와 일치합니다.
  2. state를 다른 값으로 업데이트하고 리렌더링을 촉발할 수 있는 set 함수입니다.

주의해야할 점

  • useState는 Hook이므로 컴포넌트의 최상위 레벨이나 직접 만든 Hook(custom Hook)에서만 호출할 수 있습니다.
    반복문이나 조건문 안에서는 호출할 수 없습니다. 필요한 경우 새 컴포넌트를 만들고 state를 그 안으로 옮겨야 합니다.
  • Strict Mode에서 React는 의도치 않은 실수를 찾기 위해 초기화 함수를 두 번 호출합니다.
    이는 개발 환경 전용 동작이며 프로덕션 환경에는 영향을 미치지 않습니다.
    초기화 함수가 순수하다면 동작에 영향을 미치지 않고, 호출 중 하나의 결과는 무시됩니다.
    (그래서 로그를 찍었을때 두번씩 찍히는거였구나..!)

setState(nextState)와 같은 set 함수

useState 가 반환하는 set 함수를 사용하면 state를 다른 값으로 업데이트하고 리렌더링을 하게 만들 수 있습니다. 여기에는 다음 state를 직접 전달하거나, 이전 state로부터 계산한 함수를 전달할 수도 있습니다.

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

function handleClick() {
  setName('Taylor');
  setAge(a => a + 1);
  // ...

매개변수

  • nextState : state가 될 값입니다. 값은 모든 데이터 타입이 허용되지만, 함수에 대해서는 특별한 동작이 있습니다.
    • 함수를 nextState로 전달하면 업데이터 함수로 취급합니다. 이 함수는 순수해야 하고,
      대기 중인 state를 유일한 인수로 사용해야 하며, 다음 state를 반환해야 합니다.
      React는 업데이터 함수를 대기열에 넣고 컴포넌트를 리렌더링 합니다.
      다음 렌더링 중에 React는 대기열에 있는 모든 업데이터를 이전 state에 적용하여 다음 state를 계산합니다.
// age가 42라고 가정하였을때 아래 함수는 예상대로 동작하지 않는다.
// 이는 set 함수를 호출해도 이미 실행 중인 코드에서 age state 변수가 업데이트 되지 않기 때문이다!
function handleClick() {
  setAge(age + 1); // setAge(42 + 1)
  setAge(age + 1); // setAge(42 + 1)
  setAge(age + 1); // setAge(42 + 1)
}
// 따라서 setAge에 업데이터 함수를 전달해야 한다.
// 여기서 a => a + 1은 업데이터 함수이며, 이 함수는 대기 중인 state를 가져와서 다음 state를 계산한다.
function handleClick() {
  setAge(a => a + 1); // setAge(42 => 43)
  setAge(a => a + 1); // setAge(43 => 44)
  setAge(a => a + 1); // setAge(44 => 45)
}

반환값

set 함수는 반환값이 없습니다.

주의사항

  • set 함수는 다음 렌더링에 대한 state 변수만 업데이트합니다.
    set 함수를 호출한 후에도 state 변수에는 여전히 호출 전 화면에 있던 이전 값이 담겨 있습니다.
    (그 이유는 state가 스냅샷처럼 동작하기 때문인데, 이에 대해서는 다음 포스팅에서 더 자세히 알아보겠습니다.)

  • 사용자가 제공한 새로운 값이 Object.is에 의해 현재 state 와 동일하다고 판정되면,
    React는 컴포넌트와 그 자식을들 리렌더링 하지 않습니다.

    이것이 바로 최적화이며, 경우에 따라 React가 자식을 건너뛰기 전에 컴포넌트를 호출해야 할 수도 있지만,
    코드에 영향을 미치지는 않습니다.
    ( 매우매우 중요한 부분이라고 생각되는데, Object.is() 메서드로 리렌더링 여부를 파악한다고 한다.
    이는 불변성을 유지하며 새로운 데이터를 set 해줄때 spread operator 와 함께 사용해 메모리에서 같은 객체를 참조하지 않게하고, 그로 하여금 객체가 바뀐 상태를 React에게 알려주어 리렌더링을 하게하는 이유인 것이다! )

  • React는 state 업데이트를 batch 합니다. 모든 이벤트 핸들러가 실행되고 set 함수를 호출한 후에 화면을 업데이트합니다. 이렇게 하면 단일 이벤트 중에 여러번 리렌더링 하는 것을 방지할 수 있습니다.
    드물지만 DOM에 접근하기 위해 React가 화면을 더 일찍 업데이트하도록 강제해야 하는 경우,
    flushSync 를 사용할 수 있습니다.

  • 렌더링 도중 set 함수를 호출하는 것은 현재 렌더링 중인 컴포넌트 내에서만 허용됩니다.
    React는 해당 출력을 버리고 즉시 새로운 state로 다시 렌더링을 시도합니다.
    이 패턴은 거의 필요하지 않지만 이전 렌더링의 정보를 저장하는 데 사용할 수 있습니다.

  • Strict Mode에서 React는 의도치 않은 실수를 찾기 위해 업데이터 함수를 두 번 호출합니다.
    이는 개발 환경 전용 동작이며 프로덕션 환경에는 영향을 미치지 않습니다.
    만약 업데이터 함수가 순수하다면 동작에 영향을 미치지 않습니다. 호출 중 하나의 결과는 무시됩니다.
    (업데이터 함수도 마찬가지)

공식문서를 살펴보면서 느낀 점

역시 공식문서다!

마운트시 왜 로그가 두번 찍히는지, state를 업데이트하였는데도 이전값이 계속 출력되는지,
리렌더링이 발생하는 조건이 무엇인지에 대해 더욱 정확히 알게되었습니다.

리서치를 하며 다른 포스트를 보는것보다 공식 문서를 한 번 읽는게 정확하고 빠르게 정보를 얻는 방법임을 다시 한 번 느꼈습니다.

더 알아보아야하는 useState의 동작 원리, 사용법

위에서 어느정도 훅에대해 정리하였지만 정확히 왜 이렇게 작동하는지, 사용은 어떻게 하는지에 대해서도 정리를 하며 공부하는 과정이 필요할 것 같습니다.
다음 포스팅에서도 역시 공식문서와 함께 useState 훅을 더욱 깊게 파헤쳐보겠습니다.

0개의 댓글