- 화면의 일부 항목은 입력에 따라 업데이트 된다.
- 이번 챕터에서는 state를 업데이트하고, 시간에 따라 다른 값을 보여주는 컴포넌트를 작성하는 방법을 배울 것
1. 이벤트에 응답하기
- 이벤트 핸들러는 사용자 상호작용에 반응해서 호출되는 자체 함수다
- <button> 과 같은 기본 제공 컴포넌트는 onClick 과 같은 기본 제공 브라우저 이벤트만을 지원한다
- 하지만, 컴포넌트 생성하고 이벤트 핸들러 props 로 원하는 이름을 지정할 수 있
function Toolbar({ onPlayMovie, onUploadImage }) {
return (
<div>
<Button onClick={onPlayMovie}>
Play Movie
</Button>
<Button onClick={onUploadImage}>
Upload Image
</Button>
</div>
);
}
export default function App() {
return (
<Toolbar
onPlayMovie={() => alert('Playing!')}
onUploadImage={() => alert('Uploading!')}
/>
);
}
2. State : 컴포넌트의 메모리
- 컴포넌트는 상호 작용의 결과로 화면 내용을 변경해야 하는 경우가 많다
- 이렇게 달라지는 값들은 따로 기억(저장)하고 있어야 하며 이런 컴포넌트별 메모리를 state 라고 부른다
- 컴포넌트에 state 를 추가하는 방법은 useState 훅을 사용함녀 된다
const [index, setIndex] = useState(0);
const [showMore, setShowMore] = useState(false);
- 이미지 갤러리를 만들고 이 갤러리를 클릭했을 때 state 를 사용하고 업데이트 하는 방법은 다음의 코드로 구현 가능하다
import { useState } from 'react';
import { sculptureList } from './data.js';
export default function Gallery() {
const [index, setIndex] = useState(0);
const [showMore, setShowMore] = useState(false);
const hasNext = index < sculptureList.length - 1;
function handleNextClick() {
if (hasNext) {
setIndex(index + 1);
} else {
setIndex(0);
}
}
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}
/>
</>
);
}
3. 렌더링하고 커밋하기
- 컴포넌트가 화면에 표시되기 전에 컴포넌트들은 React 에서 렌더링해야 한다.
- UI 를 요청하고 제공하는 과정은 세 단계로 이뤄진다
- Trigger
- 렌더링 발동
- Render
- 컴포넌트 렌더링
- Commit
- 렌더링된 내용을 DOM 에 커밋
4. 여러 state 업데이트를 큐에 담기
import { useState } from 'react';
export default function Counter() {
const [score, setScore] = useState(0);
function increment() {
setScore(score + 1);
}
return (
<>
<button onClick={() => increment()}>+1</button>
<button onClick={() => {
increment();
increment();
increment();
}}>+3</button>
<h1>Score: {score}</h1>
</>
)
}
- 이 컴포넌트에는 버그가 있다.
- +3 버튼을 클릭해도 점수가 1씩밖에 증가하지 않는다 (기대하는 바는 +1+1+1 해서 총 +3 이 되길 원했던 것)
- 이런 현상이 발생하는 이유에 대해서는 해당 챕터에서 다룰 예정
- 간단히 설명하면 state 를 설정하면 새로운 렌더링을 요청하지만, 이미 실행 중인 코드에서는 변경하지 않는다
- 즉,
console.log(score); // 0 setScore(score + 1); // setScore(0 + 1); console.log(score); // 0 setScore(score + 1); // setScore(0 + 1); console.log(score); // 0 setScore(score + 1); // setScore(0 + 1); console.log(score); // 0
- 위의 상태가 지속되는 것
- state 를 설정할 때 업데이터 함수를 전달하면 이 문제를 해결할 수 있다
import { useState } from 'react';
export default function Counter() {
const [score, setScore] = useState(0);
function increment() {
setScore(s => s + 1);
}
return (
<>
<button onClick={() => increment()}>+1</button>
<button onClick={() => {
increment();
increment();
increment();
}}>+3</button>
<h1>Score: {score}</h1>
</>
)
}
- 이렇게 업데이트 함수를 전달하면, 여러 state 업데이트를 큐에 넣는 방식으로 작동한다.
5. 객체 state 업데이트
- state 는 객체를 포함한 모든 JavaScript 의 값을 보유할 수 있다.
- 하지만, 객체 state 를 직접 변경해서는 안 된다.
- 반드시, 새 객체를 생성하거나 기존 객체의 복사본을 만들어 다음의 해당 복사본을 사용하도록 Update 해야 한다
- 일반적으로는 Spread Syntax(…) 를 사용한다.
import { useState } from 'react';
export default function Form() {
const [person, setPerson] = useState({
name: 'Niki de Saint Phalle',
artwork: {
title: 'Blue Nana',
city: 'Hamburg',
image: '<https://i.imgur.com/Sd1AgUOm.jpg>',
}
});
function handleNameChange(e) {
setPerson({
...person,
name: e.target.value
});
}
function handleTitleChange(e) {
setPerson({
...person,
artwork: {
...person.artwork,
title: e.target.value
}
});
}
function handleCityChange(e) {
setPerson({
...person,
artwork: {
...person.artwork,
city: e.target.value
}
});
}
function handleImageChange(e) {
setPerson({
...person,
artwork: {
...person.artwork,
image: e.target.value
}
});
}
return (
<>
Name:
Title:
City:
Image:
{person.artwork.title} {' by '} {person.name}
(located in {person.artwork.city})
);
}
- 코드에서 객체를 복사하는 것이 지루하다면 Immer 와 같은 라이브러리의 사용도 추천할 만 하다.
import { useImmer } from 'use-immer';
export default function Form() {
const [person, updatePerson] = useImmer({
name: 'Niki de Saint Phalle',
artwork: {
title: 'Blue Nana',
city: 'Hamburg',
image: '<https://i.imgur.com/Sd1AgUOm.jpg>',
}
});
function handleNameChange(e) {
updatePerson(draft => {
draft.name = e.target.value;
});
}
function handleTitleChange(e) {
updatePerson(draft => {
draft.artwork.title = e.target.value;
});
}
function handleCityChange(e) {
updatePerson(draft => {
draft.artwork.city = e.target.value;
});
}
function handleImageChange(e) {
updatePerson(draft => {
draft.artwork.image = e.target.value;
});
}
return (
<>
Name:
Title:
City:
Image:
{person.artwork.title} {' by '} {person.name}
(located in {person.artwork.city})
);
}
6. 배열 state 업데이트
- 배열은 state 에 저장할 수 있는 또 다른 유형의 Javascript 객체로 위의 객체처럼 읽기 전용으로 취급해야 한다
import { useState } from 'react';
let nextId = 3;
const initialList = [
{ id: 0, title: 'Big Bellies', seen: false },
{ id: 1, title: 'Lunar Landscape', seen: false },
{ id: 2, title: 'Terracotta Army', seen: true },
];
export default function BucketList() {
const [list, setList] = useState(
initialList
);
function handleToggle(artworkId, nextSeen) {
setList(list.map(artwork => {
if (artwork.id === artworkId) {
return { ...artwork, seen: nextSeen };
} else {
return artwork;
}
}));
}
return (
<>
<h1>Art Bucket List</h1>
<h2>My list of art to see:</h2>
<ItemList
artworks={list}
onToggle={handleToggle} />
</>
);
}
function ItemList({ artworks, onToggle }) {
return (
<ul>
{artworks.map(artwork => (
<li key={artwork.id}>
<label>
<input
type="checkbox"
checked={artwork.seen}
onChange={e => {
onToggle(
artwork.id,
e.target.checked
);
}}
/>
{artwork.title}
</label>
</li>
))}
</ul>
);
}
Reference
https://react.dev/learn/adding-interactivity
Adding Interactivity – React
The library for web and native user interfaces
react.dev
https://react-ko.dev/learn/adding-interactivity
상호작용 추가하기 – React
The library for web and native user interfaces
react-ko.dev
'(준)공식 문서 > React' 카테고리의 다른 글
[ React 공식문서 ] 상호작용 추가하기 (2) : State_컴포넌트의 메모리 (0) | 2024.03.05 |
---|---|
[ React 공식문서 ] 상호작용 추가하기 (1) : 이벤트에 응답하기 (1) | 2024.03.05 |
[ React 공식문서 ] UI 구상하기 (8) : 컴포넌트 순수성 유지 (2) | 2024.02.27 |
[ React 공식문서 ] UI 구상하기 (7) : 목록 렌더링 (2) | 2024.02.27 |
[ React 공식문서 ] UI 구상하기 (6) : 조건부 렌더링 (1) | 2024.02.27 |