블로그 구성을 보여주는 예제 글입니다.
컨트롤 하나를 더했을 뿐인데 여러 값이 함께 바뀌어야 한다면, 상태의 모양을 다시 살펴볼 때입니다. 화면에 보이는 모든 값을 상태로 저장하면 서로 맞춰야 할 값도 늘어납니다.
작은 차트를 예로 들어 원본 데이터와 사용자의 선택, 그리고 화면에 필요한 계산 결과를 구분해 보겠습니다.
저장할 값과 계산할 값
30일치 방문 기록이 있고, 사용자는 최근 7일 또는 30일을 선택할 수 있습니다. 실제로 저장해야 할 상태는 선택한 기간입니다. 화면에 표시할 데이터는 그 기간에서 계산할 수 있습니다.
const [period, setPeriod] = useState<7 | 30>(7);
const visibleData = records.slice(-period);
return (
<section>
<button onClick={() => setPeriod(7)}>최근 7일</button>
<button onClick={() => setPeriod(30)}>최근 30일</button>
<Chart data={visibleData} />
</section>
);
선택한 기간과 표시할 데이터를 각각 상태로 저장하면 두 값이 어긋날 여지가 생깁니다. 계산으로 구할 수 있는 값을 따로 저장하지 않으면 동기화 문제도 줄어듭니다.
상태는 사용하는 곳 가까이에
이 차트의 기간을 다른 페이지에서도 알아야 할까요? 그렇지 않다면 전역 저장소를 도입할 이유는 아직 없습니다. 해당 화면이 소유한 로컬 상태만으로 충분합니다.
상태를 가까이에 두면 다음 질문에 답하기 쉬워집니다.
- 어떤 사용자 입력이 이 값을 바꾸는가?
- 값이 바뀌면 화면의 어느 부분이 달라지는가?
- 컴포넌트가 사라지면 이 값을 유지해야 하는가?
목록을 바꿀 때 주의할 점
여러 지표를 표시하거나 숨길 때는 기존 배열이나 객체를 직접 변경하기보다 새 값을 반환합니다. 이전 값에 따라 다음 값을 만들 때는 함수형 업데이트가 흐름을 분명하게 해줍니다.
const [visible, setVisible] = useState({ visitors: true });
function toggleVisitors() {
setVisible(previous => ({
...previous,
visitors: !previous.visitors,
}));
}
작은 상태에서 시작하기
복잡한 도구가 필요해지는 순간은 분명히 있습니다. 다만 처음에는 입력과 화면의 관계를 설명할 수 있는 가장 작은 상태부터 시작해 보세요. 관계가 명확하면 나중에 상태의 범위를 넓히기도 수월합니다.
이 사이트의 차트 데모는 기간과 지표 선택을 로컬 상태로 관리합니다. 컨트롤을 바꾸면 그래프와 표가 같은 계산 결과를 함께 사용합니다.