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}
/>
</>
);
}
import { useState } from 'react';
let nextId = 3;
const initialArtists = [
{ id: 0, name: 'Marta Colvin Andrade' },
{ id: 1, name: 'Lamidi Olonade Fakeye'},
{ id: 2, name: 'Louise Nevelson'},
];
export default function List() {
const [name, setName] = useState('');
const [artists, setArtists] = useState(
initialArtists
);
function handleClick() {
const insertAt = 1; // Could be any index
const nextArtists = [
// Items before the insertion point:
...artists.slice(0, insertAt),
// New item:
{ id: nextId++, name: name },
// Items after the insertion point:
...artists.slice(insertAt)
];
setArtists(nextArtists);
setName('');
}
return (
<>
<h1>Inspiring sculptors:</h1>
<input
value={name}
onChange={e => setName(e.target.value)}
/>
<button onClick={handleClick}>
Insert
</button>
<ul>
{artists.map(artist => (
<li key={artist.id}>{artist.name}</li>
))}
</ul>
</>
);
}
function handleClick() {
const nextList = [...list];
nextList.reverse();
setList(nextList);
}
// bad
const myNextList = [...myList];
const artwork = myNextList.find(a => a.id === artworkId);
artwork.seen = nextSeen; // Problem: mutates an existing item
setMyList(myNextList);
// good
setMyList(myList.map(artwork => {
if (artwork.id === artworkId) {
// Create a *new* object with changes
return { ...artwork, seen: nextSeen };
} else {
// No changes
return artwork;
}
}));
다양한 상태의 종류들
비어있음: form의 “Submit”버튼은 비활성화되어 있습니다.
입력중: form의 “Submit”버튼이 활성화되어 있습니다.
제출중: form은 완전히 비활성화되어있고 Spinner가 표시됩니다.
성공시: form 대신 “Thank you”메세지가 표시됩니다.
실패시: ‘입력중’ 상태와 동일하지만 추가로 오류 메세지가 표시됩니다.
상태 변경을 촉발하는 요인들
사람의 입력 : 버튼 클릭, 필드 입력, 링크 이동 등
컴퓨터의 입력 : 네트워크에서 응답 도착, 시간 초과, 이미지 로딩 등
"가장 좋은 방법을 즉시 생각하기 어렵다면 가능한 모든 시각적 상태를 확실하게 다룰 수 있을 만큼 충분한 state를 추가하는 것부터 시작하세요."
필수가 아닌 state 제거하는 원칙
function Message({ messageColor }) {
const [color, setColor] = useState(messageColor);
// 이거 하지 말기
selectedItem이 아닌, selectedId로 상태 저장하기
위와 같은 원칙은 데이터베이스 구조를 정규화하는 것과 비슷함.
"state를 최대한 단순하게 만들되, 그보다 더 단순해서는 안됩니다."
state를 끌어올리려면 조정하려는 두자식 컴포넌트의 가장 가까운 공통 부모 컴포넌트를 찾아야 함
이는 source of truth가 됨
https://en.wikipedia.org/wiki/Don%27t_repeat_yourself
1. 해당 state를 기반으로 렌더링하는 모든 컴포넌트를 찾으세요.
2. 가장 가까운 공통 상위 컴포넌트, 즉,계층상 그 state의 영향을 받는 모든 컴포넌트들의 위에 있는 컴포넌트를 찾으세요.
3. state가 어디에 위치할지 결정합시다:
4. 대개 공통 부모에 state를 그대로 둘 수 있습니다.
5. 혹은 공통 부모보다 더 상위 컴포넌트에 state를 둘 수도 있습니다.
6. state를 소유할 적절한 컴포넌트를 찾지 못했다면, state를 소유하는 새 컴포넌트를 만들어 공통 부모 컴포넌트보다 상위에 추가하세요.
{
current: 0 // The value you passed to useRef
}
{catList.map(cat => (
<li
key={cat.id}
ref={(node) => {
const map = getMap();
if (node) {
map.set(cat.id, node);
} else {
map.delete(cat.id);
}
}}
>
<img
src={cat.imageUrl}
alt={'Cat #' + cat.id}
/>
</li>
))}
번외로 왜 나는 이 API를 몰랐을까 충격이다..
https://codesandbox.io/s/1dwe75?file=%2FApp.js&utm_medium=sandpack
ref를 자식에게 전달하려면 forwordRef 쓰기
flushSync 를 사용하면 그 안의 코드가 먼저 실행된 직후 돔이 업데이트됨 유용할듯 (https://codesandbox.io/s/21t7x2?file=%2FApp.js&utm_medium=sandpack)