일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 백트래킹
- 지연로딩
- 다대다
- 스프링 폼
- CHECK OPTION
- 낙관적락
- JPQL
- querydsl
- 유니크제약조건
- 다대일
- 비관적락
- 힙
- 데코레이터
- FetchType
- dfs
- 이진탐색
- 일대다
- fetch
- shared lock
- execute
- 연결리스트
- exclusive lock
- eager
- 연관관계
- SQL프로그래밍
- 즉시로딩
- 스토어드 프로시저
- BOJ
- 동적sql
- PS
- Today
- Total
흰 스타렉스에서 내가 내리지
이벤트에 응답하기 - 발생할 수 있는 버그 본문
스냅샷으로서의 상태(state)
일반적인 JavaScript 변수와 달리 React 상태(state)는 스냅샷과 유사하게 작동합니다.
상태를 설정하면 이미 있는 상태 변수가 변경되지 않고 다시 렌더링이 트리거됩니다.
console.log(count); // 0
setCount(count + 1); // 1로 다시 렌더링을 요청
console.log(count); // 여전히 0!
여러 개의 상태 업데이트를 큐에 저장하기
이 컴포넌트는 버그가 있습니다. "더하기 3"을 클릭하면 점수가 한 번만 증가합니다.
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>
</>
)
}
상태는 스냅샷으로서에서 이런 현상이 발생하는 이유에 대해 설명합니다.
상태를 설정하면 새로운 렌더링을 요청하지만 이미 실행 중인 코드에서는 변경되지 않습니다.
따라서 setScore(score + 1)를 호출한 후에도 점수는 여전히 0으로 유지됩니다.
이를 해결하기 위해 상태를 설정할 때 업데이트 함수를 전달할 수 있습니다.
setScore(score + 1)를 setScore(s => s + 1)로 바꾸면 "더하기 3" 버튼이 제대로 작동합니다.
이를 통해 여러 상태 업데이트를 큐에 저장할 수 있습니다.
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>
</>
)
}
상태에서 객체 업데이트하기
상태는 객체를 포함한 모든 종류의 JavaScript 값으로 유지할 수 있습니다.
그러나 React 상태에서 직접 객체와 배열을 변경해서는 안 됩니다.
객체와 배열을 업데이트하려면 새로운 객체를 생성하거나(또는 기존 객체의 사본을 만들거나) 상태를 새로운 객체를 사용하도록 업데이트해야 합니다.
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 (
<>
<label>
Name:
<input
value={person.name}
onChange={handleNameChange}
/>
</label>
<label>
Title:
<input
value={person.artwork.title}
onChange={handleTitleChange}
/>
</label>
<label>
City:
<input
value={person.artwork.city}
onChange={handleCityChange}
/>
</label>
<label>
Image:
<input
value={person.artwork.image}
onChange={handleImageChange}
/>
</label>
<p>
<i>{person.artwork.title}</i>
{' by '}
{person.name}
<br />
(located in {person.artwork.city})
</p>
<img
src={person.artwork.image}
alt={person.artwork.title}
/>
</>
);
}
코드에서 객체를 복사하는 작업이 지루하다면, 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 (
<>
<label>
Name:
<input
value={person.name}
onChange={handleNameChange}
/>
</label>
<label>
Title:
<input
value={person.artwork.title}
onChange={handleTitleChange}
/>
</label>
<label>
City:
<input
value={person.artwork.city}
onChange={handleCityChange}
/>
</label>
<label>
Image:
<input
value={person.artwork.image}
onChange={handleImageChange}
/>
</label>
<p>
<i>{person.artwork.title}</i>
{' by '}
{person.name}
<br />
(located in {person.artwork.city})
</p>
<img
src={person.artwork.image}
alt={person.artwork.title}
/>
</>
);
}
상태에서 배열 업데이트하기
배열은 상태에 저장할 수 있는 또 다른 유형의 변경 가능한 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>
);
}
코드에서 배열을 복사하는 작업이 지루하다면, Immer와 같은 라이브러리를 사용하여 반복적인 코드를 줄일 수 있습니다.
import { useState } from 'react';
import { useImmer } from 'use-immer';
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, updateList] = useImmer(initialList);
function handleToggle(artworkId, nextSeen) {
updateList(draft => {
const artwork = draft.find(a =>
a.id === artworkId
);
artwork.seen = nextSeen;
});
}
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>
);
}
'React' 카테고리의 다른 글
상태 로직을 리듀서로 추출하기 (0) | 2024.05.19 |
---|---|
변이를 사용하지 않고 배열 업데이트하기 (0) | 2024.05.19 |
Immer를 사용하여 간결한 업데이트 로직 작성하기 (0) | 2024.05.19 |
이벤트 핸들러 props 로 전달하기, 이벤트 전파, 이벤트 방지 (0) | 2024.05.19 |
JSX 스프레드 구문을 사용하여 props 전달하기 (0) | 2024.05.18 |