State를 얼마나 잘 구조화 하는가가 리액트 숙련도를 좌지우지함
State 구조화 원칙
state를 만들 때 더 나은 선택을 할 수 있는 선택지들이 있음
1. 연관된 state 그룹화하기 => 단일 state로 변수를 하나로 합치도록 병합하기
2. state의 모순 피하기 => 여러 state가 모순되면 제거하기
3. 불필요한 state 피하기 => props나 기존의 state 변수로 일부 정보를 계산할 수 있으면, 제외하기
4. state의 중복 피하기 => 동기화 유지가 어려우니 중복을 없애자
5. 깊게 중첩된 state 피하기 => 업데이트가 쉽지 않으니, 평탄화를 하자
결국 최종 목표는 오류 없이 상태를 쉽게 업데이트 하는 것임
아이슈타인이 아래와 같은 말을 했다.
당신의 state를 가능한 단순하게 만들어야한다. 더 단순하게가 아니라
근데 이거 진짜임 ??
Everything should be made as simple as possible, but no simpler (모든건 가능한 단순해야한다. 더 단순하게가 아니라)
아니네
연관된 state 그룹화하기
// 이렇게 할까 ?
const [x, setX] = useState(0);
const [y, setY] = useState(0);
// 이렇게 할까 ?
const [position, setPosition] = useState({ x: 0, y: 0 });
둘 다 되지만, 두 개의 state 변수가 함께 변경된다면, 단일 state 변수로 통합하는 것이 좋음
사용자가 폼을 사용할 때 커스텀 필드를 추가하게 되면 그룹화하는게 좋음
참고로 객체인 경우 다른 필드를 복사하지 않곤 하나의 필드만 업데이트 할 수 없음.
State의 모순 피하기
특정 변수가 동시에 하나의 값을 로직상으로 가질 수 없는 경우엔, status와 같은 state 변수를 두는게 좋다.
불필요한 state 피하기
props나 기존 state 변수로 일부 정보를 계산할 수 있으면, state를 새로 만들지 말자.
firstName / lastName / fullName
여기서 fullName은 필요없겠지 ??
Props를 state에 미러링하지 마세요.
이게 뭔소리냐
function Message({ messageColor })
const [color, setColor] = useState(messageColor);
이런 코드가 있으면, color가 messageColor로 초기화가 되는데, 문제는 부모에서 나중에 다른 값으로 messageColor를 전달하더라도 color state 변수가 업데이트 되지 않음.
state는 첫 번째 렌더링 중에만 초기화된다.
그러니 prop을 미러링하지말자.
근데 원할 때가 있을 수 있음
그럴 때는 initial / default라는 prefix를 붙여서 사용하자. 단, 일회용 느낌으로
걍 안 쓰는게 좋을거 같음 ;;
State의 중복 피하기
import { useState } from 'react';
const initialItems = [
{ title: 'pretzels', id: 0 },
{ title: 'crispy seaweed', id: 1 },
{ title: 'granola bar', id: 2 },
];
export default function Menu() {
const [items, setItems] = useState(initialItems);
const [selectedItem, setSelectedItem] = useState(
items[0]
);
...
}
이런식으로 했을 때, items가 변경된다하더라도 selectedItem이 변경되지 않는다.
그래서 selectedId를 만들고 이를 이용해서 items에서 찾아사용하면 중복을 피할 수 있다.
깊게 중첩된 state 피하기
여기서 얘기했듯이 state를 업데이트 하고 싶을 때, 복사본을 만들어야함
이게 깊으면 깊을 수록, 코드가 복잡해지니 평탄화를 노리자.
근데, filter로 걸렀으면 자동으로 GC가 수거해가는거 아닌가 ??
첫번째
import { useState } from 'react';
export default function Clock(props) {
return (
<h1 style={{ color: props.color }}>
{props.time}
</h1>
);
}
두 번째
import { useState } from 'react';
import AddItem from './AddItem.js';
import PackingList from './PackingList.js';
let nextId = 3;
const initialItems = [
{ id: 0, title: 'Warm socks', packed: true },
{ id: 1, title: 'Travel journal', packed: false },
{ id: 2, title: 'Watercolors', packed: false },
];
export default function TravelPlan() {
const [items, setItems] = useState(initialItems);
function handleAddItem(title) {
setItems([
...items,
{
id: nextId++,
title: title,
packed: false
}
]);
}
function handleChangeItem(nextItem) {
setItems(items.map(item => {
if (item.id === nextItem.id) {
return nextItem;
} else {
return item;
}
}));
}
function handleDeleteItem(itemId) {
setItems(
items.filter(item => item.id !== itemId)
);
}
return (
<>
<AddItem
onAddItem={handleAddItem}
/>
<PackingList
items={items}
onChangeItem={handleChangeItem}
onDeleteItem={handleDeleteItem}
/>
<hr />
<b>{items.filter(item => item.packed).length} out of {items.length} packed!</b>
</>
);
}
세 번째
import { useState } from 'react';
import { initialLetters } from './data.js';
import Letter from './Letter.js';
export default function MailClient() {
const [letters, setLetters] = useState(initialLetters);
const [highlightedLetter, setHighlightedLetter] = useState(null);
function handleHover(letter) {
setHighlightedLetter(letter.id);
}
function handleStar(starred) {
setLetters(letters.map(letter => {
if (letter.id === starred.id) {
return {
...letter,
isStarred: !letter.isStarred
};
} else {
return letter;
}
}));
}
return (
<>
<h2>Inbox</h2>
<ul>
{letters.map(letter => (
<Letter
key={letter.id}
letter={letter}
isHighlighted={
letter.id === highlightedLetter
}
onHover={handleHover}
onToggleStar={handleStar}
/>
))}
</ul>
</>
);
}
해당 이슈가 발생하는 이유는 highlightedLetter 문자 객체를 보관하고 있다는 점이 문제임
즉 letters가 변경되면 highlightedLetter 문자 객체완ㄴ 다른 새 객체가 발생해서 그런거임
네 번째
import { useState } from 'react';
import { letters } from './data.js';
import Letter from './Letter.js';
export default function MailClient() {
const [selectedIds, setSelectedIds] = useState([]);
const selectedCount = selectedIds.length;
function handleToggle(toggledId) {
if (selectedIds.includes(toggledId)) {
setSelectedIds(selectedIds.filter(id => id !== toggledId));
} else {
setSelectedIds([...selectedIds, toggledId]);
}
}
return (
<>
<h2>Inbox</h2>
<ul>
{letters.map(letter => (
<Letter
key={letter.id}
letter={letter}
isSelected={
// TODO: allow multiple selection
selectedIds.includes(letter.id)
}
onToggle={handleToggle}
/>
))}
<hr />
<p>
<b>
You selected {selectedCount} letters
</b>
</p>
</ul>
</>
);
}
저번 시간과 마찬가지로 리액트가 아닌 툴을 쓸 때도 이 내용을 머리에 심고 개발하는게 좋을거 같다.
'Web + APP > React' 카테고리의 다른 글
[리액트 스터디] - Reacting to Input with State (0) | 2024.07.27 |
---|---|
[리액트 스터디] - Updating Arrays in State (0) | 2024.07.26 |
[리액트 스터디] - Queueing a Series of State Updates (0) | 2024.07.18 |
[리액트 스터디] - State as a Snapshot (2) | 2024.07.18 |
Flux 패턴 (0) | 2021.03.19 |