Stores
Writable stores
Svelte에서는 store를 통해 상태 로직을 컴포넌트와 분리할 수 있다. store는 상태값이 변경되었을 때 관련 컴포넌트들에게 알려주는 subscribe
메서드를 가진 단순한 객체다.
store 중에는 writable store가 있으며, 이는 set
과 update
메서드를 갖는다.
<script>
import { count } from './stores.js';
import Incrementer from './Incrementer.svelte';
import Decrementer from './Decrementer.svelte';
import Resetter from './Resetter.svelte';
let count_value;
const unsubscribe = count.subscribe(value => {
count_value = value;
});
</script>
update
는 인수를 콜백함수로 받으며, set
는 직접적인 값을 받는다.
function increment() {
count.update(n => n + 1);
}
function reset() {
count.set(0);
}
Auto-subscriptions
만약, 컴포넌트가 초기화와 제거를 여러번 반복하게 되는 경우, unsubscribe
함수를 실행하지 않게되면 메모리 누수의 위험이 있다.
따라서, 이를 방지하기 위해선 onDestroy
생명주기 메서드에서 이를 호출해주어야 한다.
<script>
import { onDestroy } from 'svelte';
import { count } from './stores.js';
import Incrementer from './Incrementer.svelte';
import Decrementer from './Decrementer.svelte';
import Resetter from './Resetter.svelte';
let count_value;
const unsubscribe = count.subscribe(value => {
count_value = value;
});
onDestroy(unsubscribe);
</script>
<h1>The count is {count_value}</h1>
헌데, 여러 컴포넌트에 대해 동일한 작업을 해주어야 하는 상황이라면 이런 방법이 다소 번거롭게 느껴질 수 있다.(boilerplatey) Svelte는 ``만 덧붙이면 store 값에 대해 앞선 작업들을 알아서 처리해준다.
<script>
import { count } from './stores.js';
import Incrementer from './Incrementer.svelte';
import Decrementer from './Decrementer.svelte';
import Resetter from './Resetter.svelte';
</script>
<h1>The count is {count}</h1>
이러한 Auto-subscription은 store의 변수들이 컴포넌트 최상위 스코프에서 선언 및 import 되었을 때만 제대로 동작한다.
<span class="katex"><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8888799999999999em;vertical-align:-0.19444em;"></span><span class="mord">‘</span><span class="mord hangul_fallback">로변수명을시작하는것은</span><span class="mord mathnormal">s</span><span class="mord mathnormal">t</span><span class="mord mathnormal">ore</span><span class="mord hangul_fallback">값을참조하겠다는것으로간주되며</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord hangul_fallback">그렇지않은경우에대해</span><span class="mord mathnormal" style="margin-right:0.05764em;">S</span><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="mord mathnormal">e</span><span class="mord mathnormal">lt</span><span class="mord mathnormal">e</span><span class="mord hangul_fallback">는</span><span class="mord">‘</span></span></span></span>
로 임의의 변수를 선언하는 것을 방지한다.
Readable stores
모든 경우에 store를 참조하는 컴포넌트들에게 쓰기 권한을 부여할 필요는 없다. 이를테면, 시간, 마우스 위치, 지리적 위치 등을 다루는 경우가 그렇다.
이러한 경우에 readable store를 사용할 수 있다.
export const time = readable(new Date(), function start(set) {
const interval = setInterval(() => {
set(new Date());
}, 1000);
return function stop() {
clearInterval(interval);
};
});
readable
의 첫번째 인수는 초기값이며, 설정할 필요가 없다면 null
혹은 undefined
로 두면 된다.
두번째 인수는 start
콜백함수이며, 이는 set
콜백함수를 파라미터로 받고 stop
함수를 반환하는 함수다. start
함수는 최초의 subscriber에 의해 상태값이 참조되는 경우에 호출되며, stop
은 마지막 subscriber가 unscribe를 했을 때에 실행되는 cleanup 함수다.
Derived stores
특정 store의 값에 의존하는 다른 값이 있는 경우, derived
를 통해 새로운 store를 만들어 줄 수 있다. 아래는 특정 페이지가 열리고 나서의 시간을 측정한 상태값을 보유한 derived store다.
export const elapsed = derived(
time,
<span class="katex"><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.69862em;vertical-align:-0.0391em;"></span><span class="mord mathnormal">t</span><span class="mord mathnormal">im</span><span class="mord mathnormal">e</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.10903em;">M</span><span class="mord mathnormal">a</span><span class="mord mathnormal">t</span><span class="mord mathnormal">h</span><span class="mord">.</span><span class="mord mathnormal">ro</span><span class="mord mathnormal">u</span><span class="mord mathnormal">n</span><span class="mord mathnormal">d</span><span class="mopen">((</span></span></span></span>time - start) / 1000)
);
여러 inputs들로부터 derive store를 갖추고, 값을 반환하는 대신 명시적으로 set
를 통해 값을 변경해줄 수도 있다. 이에 대해서는 여기를 참조하자.
Custom stores
어떤 객체든 subscribe
메서드가 적절하게 실행되기만 하면, 이는 store로 취급된다. 이를 통해 유저가 임의로 커스텀 store를 만들어 로직을 처리할 수 있다.
해당 문서의 앞쪽에서 writable
을 통해 store를 다루었던 내용을 커스텀 store를 통해 리팩토링해보자.
function createCount() {
const { subscribe, set, update } = writable(0);
return {
subscribe,
increment: () => update(n => n + 1),
decrement: () => update(n => n - 1),
reset: () => set(0)
};
}
Store bindings
writable store를 사용하는 경우, 로컬 컴포넌트의 상태값과 store의 값을 binding 해줄 수 있다.
아래 예시에서는 name
store 값을 input
의 value
값에 바인딩을 해주는 예시다.
<input bind:value={name}>
이후, input의 value
에 변경이 있다면 name
store값 역시 자동으로 업데이트된다.
컴포넌트 내부에서 값을 직접 할당해줄 수도 있다.
<button on:click="{() => name += '!'}">
Add exclamation mark!
</button>
위에서의 name += '!'
은 `name.set(