컴포넌트는 상호 작용의 결과로 화면의 내용을 변경해야 하는 경우가 많습니다. 폼에 입력하면 입력 필드가 업데이트되어야 하고, 이미지 캐러셀에서 '다음'을 클릭하면 표시되는 이미지가 변경되어야 하며, '구매'를 클릭하면 제품이 장바구니에 담겨야 합니다. 컴포넌트는 현재 입력값, 현재 이미지, 장바구니와 같은 것들을 "기억"해야 합니다. React에서는 이런 종류의 컴포넌트별 메모리를 state라고 부릅니다.
학습 내용
useState
훅으로 state 변수를 추가하는 방법useState
훅이 반환하는 값 쌍- state 변수를 두 개 이상 추가하는 방법
- state를 지역적이라고 하는 이유
다음은 조각상 이미지를 렌더링하는 컴포넌트입니다. "Next" 버튼을 클릭하면 index
를 1
, 2
로 변경하여 다음 조각상을 표시해야 합니다. 그러나 이것은 작동하지 않습니다. (시도해보세요!)
handleClick
이벤트 핸들러가 지역 변수 index
를 업데이트하고 있습니다. 하지만 두 가지 이유로 인해 변경 사항이 표시되지 않습니다:
컴포넌트를 새 데이터로 업데이트하려면 두 가지 작업이 필요합니다:
useState
훅은 이 두 가지를 제공합니다:
state 변수를 추가하려면 파일 상단에 있는 React에서 useState
를 import합니다.
그런 다음 이 줄을.
다음과 같이 바꿉니다.
index
는 state 변수이고 setIndex
는 설정자 함수입니다.
여기서
[
와]
구문을 배열 구조분해라고 하며, 배열에서 값을 읽을 수 있습니다.useState
가 반환하는 배열에는 항상 정확히 두 개의 항목이 있습니다.
이것이 handleClick
에서 함께 작동하는 방식입니다.
이제 "Next" 버튼을 클릭하면 현재 조각상이 바뀝니다.
React에서는 useState
를 비롯해 "use
"로 시작하는 다른 함수를 훅(hook)이라고 부릅니다.
훅은 React가 렌더링 중일 때만 사용할 수 있는 특별한 함수입니다 (다음 페이지에서 더 자세히 설명하겠습니다). 이를 통해 다양한 React 기능을 "연결"할 수 있습니다.
state는 이러한 기능 중 하나일 뿐이며, 나중에 다른 훅들은 만나게 될 것입니다.
⚠️ Pitfall
훅(
use
로 시작하는 함수)은 “컴포넌트의 최상위 레벨” (최상위 컴포넌트 아님) 또는 [커스텀 훅](https://www.notion.so/97384c8717b84197b1aae1872e1c41b0)에서만 호출할 수 있습니다. 조건, 루프 또는 기타 중첩된 함수 내부에서는 훅을 호출할 수 없습니다. 훅은 함수이지만 컴포넌트의 필요에 대한 무조건적인 선언으로 생각하면 도움이 됩니다. 파일 상단에서 모듈을 "import"하는 것과 유사하게 컴포넌트 상단에서 React 기능을 "사용"합니다.
useState
를 호출하는 것은, React에게 이 컴포넌트가 무언가를 기억하기를 원한다고 말하는 것입니다.
이 경우 React가 index를 기억하기를 원합니다.
Note
이 쌍의 이름은
const [something, setSomething]
과 같이 지정하는 것이 일반적입니다. 원하는 대로 이름을 지을 수 있지만, 프로젝트 전반에서 이해하기 쉽도록 규칙을 정합니다.
useState
의 유일한 인수는 state 변수의 초기값입니다. 이 예제에서는 useState(0)
에 의해 index
의 초기값이 0
으로 설정되어 있습니다.
컴포넌트가 렌더링될 때마다 useState
는 두 개의 값을 포함하는 배열을 제공합니다:
index
).setIndex
).실제 작동 방식은 다음과 같습니다:
index
의 초기값으로 0
을 useState
에 전달했으므로 [0, setIndex]
가 반환됩니다. React는 0
을 최신 state 값으로 기억합니다.setIndex(index + 1)
를 호출합니다. index
는 0
이므로 setIndex(1)
입니다. 이렇게 하면 React는 이제 index
가 1
임을 기억하고 다른 렌더링을 트리거합니다.useState(0)
을 보지만, index
를 1
로 설정한 것을 기억하고 있기 때문에, 이번에는 [1, setIndex]
를 반환합니다.하나의 컴포넌트에 원하는 만큼 많은 유형의 state 변수를 가질 수 있습니다. 이 컴포넌트에는 숫자 타입 index
와, '세부 정보 표시'를 클릭하면 토글되는 불리언 타입인 showMore
라는, 두 개의 state 변수가 있습니다.
이 예제에서 index
와 showMore
처럼 서로 연관이 없는 경우 여러 개의 state 변수를 갖는 것이 좋습니다. 그러나 두 개의 state 변수를 자주 함께 변경하는 경우에는 두 변수를 하나로 합치는 것이 더 좋을 수 있습니다. 예를 들어 필드가 많은 폼의 경우 필드별로 state 변수를 사용하는 것보다 객체를 값으로 하는 하나의 state 변수를 사용하는 것이 더 편리합니다. 자세한 팁은 state 구조 선택에서 확인할 수 있습니다.
- [Deep Dive] React는 어떤 state를 반환할지 어떻게 알 수 있을까요?
useState
호출이 어떤 state 변수를 참조하는지에 대한 정보를 받지 못한다는 것을 눈치채셨을 것입니다.useState
에 전달되는 “식별자”가 없는데 어떤 state 변수를 반환할지 어떻게 알 수 있을까요? 함수를 파싱하는 것과 같은 마법에 의존할까요? 대답은 '아니요'입니다.대신 간결한 구문을 구현하기 위해 훅은 동일한 컴포넌트의 모든 렌더링에서 안정적인 호출 순서에 의존합니다. 위의 규칙("최상위 수준에서만 훅 호출")을 따르면, 훅은 항상 같은 순서로 호출되기 때문에 실제로 잘 작동합니다. 또한 linter 플러그인은 대부분의 실수를 잡아줍니다.
내부적으로 React는 모든 컴포넌트에 대해 한 쌍의 state 배열을 가집니다. 또한 렌더링 전에
0
으로 설정된 현재 쌍 인덱스를 유지합니다.useState
를 호출할 때마다 React는 다음 state 쌍을 제공하고 인덱스를 증가시킵니다. 이 메커니즘에 대한 자세한 내용은 React Hook: 마법이 아닌 그저 배열일 뿐에서 확인할 수 있습니다.
state는 화면의 컴포넌트 인스턴스에 지역적입니다. 즉 동일한 컴포넌트를 두 군데에서 렌더링하면 각 사본은 완전히 격리된 state를 갖게 됩니다! 이 중 하나를 변경해도 다른 컴포넌트에는 영향을 미치지 않습니다.
이 예시에서는 앞의 Gallery
컴포넌트가 로직을 변경하지 않고 두 군데에서 렌더링되었습니다. 각 갤러리 내부의 버튼을 클릭해 보세요. 각각의 state가 독립적인 것을 확인할 수 있습니다:
이것이 바로 모듈 상단에 선언하는 일반 변수와 state의 차이점입니다. state는 특정 함수 호출에 묶이지 않고, 코드의 특정 위치에 묶이지도 않지만, 화면상의 특정 위치에 "지역적"입니다. 두 개의 <Gallery />
컴포넌트를 렌더링했으므로 해당 state는 별도로 저장됩니다.
Page 컴포넌트는 Gallery의 state뿐 아니라 심지어 state가 있는지 여부조차 전혀 "알지 못한다"는 점도 주목하세요. props와 달리 state는 이를 선언하는 컴포넌트 외에는 완전히 비공개이며, 부모 컴포넌트는 이를 변경할 수 없습니다. 따라서 다른 컴포넌트에 영향을 주지 않고 state를 추가하거나 제거할 수 있습니다.
두 Gallery 컴포넌트의 state를 동기화하려면 어떻게 해야 할까요? React에서 이를 수행하는 올바른 방법은 자식 컴포넌트에서 state를 제거하고 가장 가까운 공유 부모 컴포넌트에 추가하는 것입니다. 다음 몇 페이지는 단일 컴포넌트의 state를 구성하는 데 초점을 맞추겠지만, 이 주제는 컴포넌트 간 state 공유에서 다시 다룰 것입니다.
useState
훅을 호출하여 선언합니다.use
로 시작하는 특수 함수입니다. state와 같은 React 기능을 "연결"할 수 있게 해줍니다.useState
를 포함한 훅을 호출하는 것은 컴포넌트나 다른 훅의 최상위 레벨에서만 유효합니다.useState
훅은 현재 state와 이를 업데이트할 함수로 이루어진 한 쌍을 반환합니다.