컴포넌트는 상호 작용의 결과로 화면의 내용을 변경해야 하는 경우가 많다.
import { sculptureList } from './data.js';
export default function Gallery() {
let index = 0;
function handleClick() {
index = index + 1;
}
let sculpture = sculptureList[index];
return (
<>
<button onClick={handleClick}>
Next
</button>
<h2>
<i>{sculpture.name} </i>
by {sculpture.artist}
</h2>
<h3>
({index + 1} of {sculptureList.length})
</h3>
<img
src={sculpture.url}
alt={sculpture.alt}
/>
<p>
{sculpture.description}
</p>
</>
);
}
위 코드를 이용한 컴포넌트로 "Next" 버튼을 클릭해도 작동하지 않는다.
handleClick 이벤트 핸들러는 지역번수 index를 업데이트하고 있는데, 이러한 변화를 보이지 않게 하는 두 가지 이유가 있다.
=> 컴포넌트를 새로운 데이터로 업데이트 하기 위해서는
1. 렌더링 사이에 데이터를 유지
2. React가 새로운 데이터로 컴포넌트를 렌더링하도록 유발
해야한다.
useState 훅은 이 두 가지를 제공한다.
1. 렌더링 간에 데이터를 유지하기 위한 state 변수
2. 변수를 업데이트하고 React가 컴포넌트를 다시 렌더링하도록 유발하는 state setter 함수
useState 를 가져온다. import { useState } from 'react';
let index = 0;
다음과 같이 바꾼다.
const [index, setIndex] = useState(0);
index : state 변수 / setIndex : setter 함수
handleClick 함수 정의 (이벤트 핸들러에서 상태 변경하기) function handleClick() {
setIndex(index + 1);
}
React에서 useState와 같이 use로 시작하는 모든 함수를 훅이라고 한다.
훅은 React가 렌더링 중일 때만 사용할 수 있는 특별한 함수로, 이를 이용해 React의 다양한 기능을 연결할 수 있다.
📍 주의
const [index, setIndex] = useState(0);`
useState를 호출하는 것은 React에 이 컴포넌트가 무언가를 기억하기를 원한다고 말하는 것이다. (위의 경우 React가 index를 기억하기를 원함)
이 쌍의 이름은 const [something, setSomething]과 같이 지정하는 것이 규칙 !
useState의 유일한 인수는 state 변수의 초깃값이다. 위 예시에서 index의 초깃값은 useState(0)에 의해 0으로 설정된다.
useState의 작동 방식
: 컴포넌트가 렌더링될 때마다, useState는 다음 두 개의 값을 포함하는 배열을 제공한다.
index) setIndex) ⚡️ 작동 방식
1. 컴포넌트 렌더링 : index의 초깃값으로 0을 전달했으므로 [0, setIndex]가 반환된다. React는 최신 state 값으로 0을 기억한다.
2. state 업데이트 : 사용자가 버튼을 클릭하면 setIndex(index + 1)을 호출한다. index는 0이므로 setIndex(1)이다. 이는 React에 index = 1 임을 기억하게 하고, 또 다른 렌더링을 유발한다.
3. 컴포넌트 두 번째 렌더링 : React는 여전히 useState(0)을 보지만, index를 1로 설정한 것을 기억하고 있기 때문에, 이번에는 [1, setIndex]를 반환함.
4. 계속 ../
import { useState } from 'react';
import { sculptureList } from './data.js';
export default function Gallery() {
const [index, setIndex] = useState(0);
const [showMore, setShowMore] = useState(false);
function handleNextClick() {
setIndex(index + 1);
}
function handleMoreClick() {
setShowMore(!showMore);
}
let sculpture = sculptureList[index];
return (
<>
<button onClick={handleNextClick}>
Next
</button>
<h2>
<i>{sculpture.name} </i>
by {sculpture.artist}
</h2>
<h3>
({index + 1} of {sculptureList.length})
</h3>
<button onClick={handleMoreClick}>
{showMore ? 'Hide' : 'Show'} details
</button>
{showMore && <p>{sculpture.description}</p>}
<img
src={sculpture.url}
alt={sculpture.alt}
/>
</>
);
}
index와 불리언 타입 showMore 라는 두 개의 state 변수const [index, setIndex] = useState(0);
const [name, setName] = useState('');
Q) useState안에 index같은 식별자를 넘기지 않는데, React는 어떤 state가 누구 건지 어떻게 구분할까?
A) 호출 순서 로 구분한다.
useState(0); // 1번째
useState(''); // 2번째
useState(false); // 3번째
컴포넌트 안에서 React는 아래와 같은 배열을 가지고 있다.
state = [0, '', false]
그리고 렌더링 시작할 때, currentIndex = 0;로 시작하고,
useState가 호출될 때 내부적으로 아래와 같이 동작한다.
1. state[currentIndex] 반환
2. currentIndex ++
3. 다음 useState는 그 다음 칸을 사용
이러한 이유 때문에 최상위에서만 훅 호출 규칙이 중요한데, 만약
if (something) {
useState(0);
}
이런 식으로 사용하면, 렌더링마다 호출 순서가 바뀔 수 있기 때문이다.
Gallery.js
import { useState } from 'react';
import { sculptureList } from './data.js';
export default function Gallery() {
const [index, setIndex] = useState(0);
const [showMore, setShowMore] = useState(false);
function handleNextClick() {
setIndex(index + 1);
}
function handleMoreClick() {
setShowMore(!showMore);
}
let sculpture = sculptureList[index];
return (
<section>
<button onClick={handleNextClick}>
Next
</button>
<h2>
<i>{sculpture.name} </i>
by {sculpture.artist}
</h2>
<h3>
({index + 1} of {sculptureList.length})
</h3>
<button onClick={handleMoreClick}>
{showMore ? 'Hide' : 'Show'} details
</button>
{showMore && <p>{sculpture.description}</p>}
<img
src={sculpture.url}
alt={sculpture.alt}
/>
</section>
);
}
App.js
import Gallery from './Gallery.js';
export default function Page() {
return (
<div className="Page">
<Gallery />
<Gallery />
</div>
);
}
동일한 컴포넌트를 두 번 렌더링한다면 각 복사본은 완전히 격리된 state를 갖는다. 즉, 그 중 하나를 변경해도 다른 하나에는 영향을 미치지 않는다.
위의 예시에서 Gallery 컴포넌트가 두 번 렌더링되었는데, 각각 Gallery 내부 버튼을 클릭해보면 그들의 state가 서로 독립적임을 알 수 있다.
<Gallery/> 컴포넌트를 두 번 렌더링했으므로 그들의 state는 별도로 저장된다. function Page() {
return <Gallery />;
}
function Gallery() {
const [index, setIndex] = useState(0);
}
Gallery 안에 index라는 state가 존재하지만, Page는 그것을 전혀 알지 못한다💥 vs Props
function Page() {
return <Gallery color="red" />;
}
color은 부모가 넘겨준 값으로, 부모가 컨트롤 가능하다는 의미이다✔️ Props = 외부에서 들어오는 값
✔️ State = 그 컴포넌트만의 내부 기억