State: 컴포넌트의 메모리
컴포넌트는 종종 상호작용의 결과로 화면에 표시되는 내용을 변경해야 합니다. 폼에 입력하면 입력 필드가 업데이트되어야 하고, 이미지 캐러셀에서 "다음"을 클릭하면 표시되는 이미지가 변경되어야 하며, "구매"를 클릭하면 제품이 장바구니에 담겨야 합니다. 컴포넌트는 현재 입력값, 현재 이미지, 장바구니와 같은 것들을 "기억"해야 합니다. React에서 이러한 종류의 컴포넌트별 메모리를state라고 합니다.
배울 내용
- 어떻게 useStateHook으로 state 변수를 추가하는지
useStateHook이 반환하는 값 쌍은 무엇인지- 하나 이상의 state 변수를 어떻게 추가하는지
- 왜 state를 지역(local)이라고 부르는지
일반 변수로는 부족할 때
다음은 조각상 이미지를 렌더링하는 컴포넌트입니다. "Next" 버튼을 클릭하면index를 1, 그다음2등으로 변경하여 다음 조각상을 표시해야 합니다. 그러나 이 코드는작동하지 않습니다(직접 시도해 볼 수 있습니다!):
handleClick이벤트 핸들러는 지역 변수인index를 업데이트하고 있습니다. 그러나 두 가지 이유로 인해 이 변경 사항이 표시되지 않습니다:
- 지역 변수는 렌더링 사이에 유지되지 않습니다.React가 이 컴포넌트를 두 번째로 렌더링할 때는 처음부터 렌더링합니다—지역 변수의 변경 사항을 고려하지 않습니다.
- 지역 변수의 변경은 렌더링을 트리거하지 않습니다.React는 새로운 데이터로 컴포넌트를 다시 렌더링해야 한다는 것을 인식하지 못합니다.
새로운 데이터로 컴포넌트를 업데이트하려면 두 가지 작업이 필요합니다:
- 렌더링 사이에데이터를 유지합니다.
- React가 새로운 데이터로 컴포넌트를 렌더링하도록 (재렌더링) 트리거합니다.
useStateHook은 이 두 가지를 제공합니다:
- 렌더링 사이에 데이터를 유지할state 변수.
- 변수를 업데이트하고 React가 컴포넌트를 다시 렌더링하도록 트리거하는state 설정 함수.
state 변수 추가하기
state 변수를 추가하려면, 파일 상단에서 React로부터useState를 import하세요:
그런 다음, 이 줄을:
다음으로 바꾸세요:
index는 state 변수이고setIndex는 설정 함수입니다.
여기서[와 ] 구문은 배열 구조 분해라고 하며 배열에서 값을 읽을 수 있게 해줍니다.useState가 반환하는 배열에는 항상 정확히 두 개의 항목이 있습니다.
이 둘이 handleClick에서 함께 작동하는 방식입니다:
이제 "Next" 버튼을 클릭하면 현재 조각상이 전환됩니다:
첫 번째 Hook 만나기
React에서useState와 “use”로 시작하는 다른 모든 함수는 Hook이라고 합니다.
Hook은 React가 렌더링중일 때만 사용할 수 있는 특별한 함수입니다(다음 페이지에서 더 자세히 설명하겠습니다). Hook을 사용하면 다양한 React 기능에 “연결”할 수 있습니다.
상태는 그런 기능 중 하나일 뿐이며, 다른 Hook은 나중에 만나게 될 것입니다.
주의
Hook—use로 시작하는 함수—는 컴포넌트의 최상위 레벨이나사용자 정의 Hook에서만 호출할 수 있습니다. 조건문, 반복문 또는 기타 중첩 함수 내부에서는 Hook을 호출할 수 없습니다. Hook은 함수이지만, 컴포넌트의 요구 사항에 대한 무조건적인 선언으로 생각하는 것이 도움이 됩니다. 파일 상단에서 모듈을 “import”하는 것과 유사하게 컴포넌트 상단에서 React 기능을 “use”합니다.
useState의 구조
useState를 호출하면 이 컴포넌트가 무언가를 기억하길 원한다고 React에게 알리는 것입니다:
이 경우, React가 index를 기억하길 원합니다.
참고
관례적으로 이 쌍을 const [something, setSomething]과 같이 명명합니다. 원하는 대로 이름을 지을 수 있지만, 관례를 따르면 프로젝트 간에 이해하기 쉬워집니다.
useState의 유일한 인수는 상태 변수의초기값입니다. 이 예제에서 index의 초기값은 useState(0)로 0으로 설정됩니다.
컴포넌트가 렌더링될 때마다,useState는 두 개의 값을 포함하는 배열을 제공합니다:
- 저장한 값을 가진상태 변수(
index). - 상태 변수를 업데이트하고 React가 컴포넌트를 다시 렌더링하도록 트리거할 수 있는상태 설정 함수(
setIndex).
실제 동작은 다음과 같습니다:
- 컴포넌트가 처음 렌더링됩니다.
0을useState에index의 초기값으로 전달했기 때문에,[0, setIndex]를 반환합니다. React는0이 최신 상태 값임을 기억합니다. - 상태를 업데이트합니다.사용자가 버튼을 클릭하면
setIndex(index + 1)를 호출합니다.index는0이므로setIndex(1)입니다. 이는 React에게 이제index가1임을 기억하고 다른 렌더링을 트리거하도록 지시합니다. - 컴포넌트의 두 번째 렌더링.React는 여전히
useState(0)을 보지만, React가기억하고 있기 때문에index를1로 설정했다는 것을 알고, 대신[1, setIndex]를 반환합니다. - 이런 식으로 계속됩니다!
컴포넌트에 여러 상태 변수 부여하기
한 컴포넌트 안에 원하는 만큼 많은 타입의 상태 변수를 가질 수 있습니다. 이 컴포넌트에는 숫자 타입의index와 불리언 타입의showMoreshowMore라는 두 개의 상태 변수가 있으며, "Show details"를 클릭하면 가 토글됩니다:
상태가 서로 관련이 없다면, 이 예시의 index와 showMore처럼 여러 개의 상태 변수를 두는 것이 좋습니다. 하지만 두 상태 변수를 자주 함께 변경하는 경우, 하나로 합치는 것이 더 쉬울 수 있습니다. 예를 들어, 많은 필드가 있는 폼의 경우 필드마다 별도의 상태 변수를 두기보다는 객체를 담는 단일 상태 변수를 사용하는 것이 더 편리합니다. 더 많은 팁은상태 구조 선택하기를 참조하세요.
State는 격리되고 비공개입니다
State는 화면에 표시되는 컴포넌트 인스턴스에 로컬입니다. 다시 말해,동일한 컴포넌트를 두 번 렌더링하면 각 복사본은 완전히 격리된 state를 갖습니다!하나를 변경해도 다른 하나에는 영향을 미치지 않습니다.
이 예시에서 이전의Gallery컴포넌트는 로직을 변경하지 않고 두 번 렌더링됩니다. 각 갤러리 내부의 버튼을 클릭해 보세요. 그들의 state가 독립적임을 확인할 수 있습니다:
이것이 상태를 모듈 상단에 선언할 수 있는 일반 변수와 구분 짓는 특징입니다. 상태는 특정 함수 호출이나 코드의 위치에 묶이지 않지만, 화면의 특정 위치에 "지역적"입니다. 두 개의<Gallery />컴포넌트를 렌더링했으므로, 그들의 상태는 별도로 저장됩니다.
또한 Page 컴포넌트가 Gallery의 상태에 대해 아무것도 "알지" 못하거나 심지어 상태가 있는지 여부도 모르는 점에 주목하세요. props와 달리,상태는 이를 선언하는 컴포넌트에 완전히 비공개입니다.부모 컴포넌트는 이를 변경할 수 없습니다. 이는 나머지 컴포넌트에 영향을 주지 않고 상태를 어떤 컴포넌트에 추가하거나 제거할 수 있게 합니다.
두 갤러리의 상태를 동기화된 상태로 유지하려면 어떻게 해야 할까요? React에서 이를 수행하는 올바른 방법은 자식 컴포넌트에서 상태를제거하고 가장 가까운 공유 부모에 추가하는 것입니다. 다음 몇 페이지는 단일 컴포넌트의 상태 구성에 초점을 맞추지만, 이 주제는에서 다시 다루겠습니다.컴포넌트 간 상태 공유
요약
- 컴포넌트가 렌더링 사이에 정보를 "기억"해야 할 때 상태 변수를 사용하세요.
- 상태 변수는
useState훅을 호출하여 선언됩니다. - 훅은
use로 시작하는 특별한 함수입니다. 이를 통해 상태와 같은 React 기능에 "연결"할 수 있습니다. - 훅은 import 문을 떠올리게 할 수 있습니다: 조건 없이 호출되어야 합니다.
useState를 포함한 훅 호출은 컴포넌트 또는 다른 훅의 최상위 레벨에서만 유효합니다. -
useState훅은 현재 상태와 이를 업데이트하는 함수, 두 개의 값을 쌍으로 반환합니다. - 하나 이상의 상태 변수를 가질 수 있습니다. 내부적으로 React는 순서에 따라 이를 매칭합니다.
- 상태는 컴포넌트에 비공개입니다. 두 곳에서 렌더링하면 각 복사본은 자신만의 상태를 가집니다.
Try out some challenges
Challenge 1 of 4:Complete the gallery #
When you press “Next” on the last sculpture, the code crashes. Fix the logic to prevent the crash. You may do this by adding extra logic to event handler or by disabling the button when the action is not possible.
After fixing the crash, add a “Previous” button that shows the previous sculpture. It shouldn’t crash on the first sculpture.
