파생 값과 이펙트
읽는 시간 약 9분
이 문서에서 배우는 것
- 계산 결과를 상태로 승격하지 않고 다루는 법
- 이펙트가 실행되는 시점과 정리 시점
- 이펙트를 남용했을 때 생기는 증상
파생 값
다른 상태로부터 계산되는 값은 state가 아니라 derived입니다. 의존하는 상태가 바뀔 때만 다시 계산되고, 아무도 읽지 않으면 계산 자체를 건너뜁니다.
import { state, derived } from 'loopkit';
const items = state([]);
const total = derived(() => items().reduce((sum, it) => sum + it.price, 0));total은 읽기 전용입니다. .set()이 없다는 점이 의도적인데, 계산식이 유일한 진실이기 때문입니다. 합계를 따로 state로 두고 손으로 맞춰 주다 보면, 어느 경로에서 갱신을 빠뜨려 화면과 데이터가 어긋나는 버그가 반드시 생깁니다.
이펙트
화면 밖으로 나가는 일 — 서버 호출, 로컬 저장소 쓰기, 타이머, 구독 — 은 effect에 둡니다. 이펙트는 처음 한 번 실행되고, 그 안에서 읽은 상태가 바뀔 때마다 다시 실행됩니다. 돌려준 함수는 다음 실행 직전과 정리 시점에 호출됩니다.
import { effect } from 'loopkit';
effect(() => {
const id = setInterval(() => tick.set(tick() + 1), 1000);
return () => clearInterval(id);
});주의
이펙트 안에서 다른 상태를 쓰고, 그 상태가 다시 같은 이펙트를 깨우면 무한 루프가 됩니다. 값을 계산해서 어딘가에 넣고 싶어질 때는 거의 항상 derived가 정답입니다.
언제 이펙트가 아닌가
사용자의 행동에 대한 반응은 이벤트 핸들러의 일이지 이펙트의 일이 아닙니다. 버튼을 눌러 저장하는 코드는 @click 안에 두세요. 이펙트에 넣으면 “왜 저장됐는지”가 코드에서 사라집니다.
요약
- 계산으로 얻을 수 있는 값은 derived로 두고 손으로 동기화하지 않는다.
- 바깥세상과의 접점만 effect에 둔다.
- 사용자 행동의 결과는 이벤트 핸들러에 남긴다.
