store
- 웹앱에서 모든 컴포넌트가 동일한 상태를 가지지 않는다.
보통 부모에서 자식에게 상태를 내려주기도 하고, 자식이 부모에게 요청해서 상태를 내려받기도 한다. 하지만 컴포넌트가 많아지거나 또는 웹앱 규모가 커지면 이런 절차가 많아지면서 복잡도가 증가한다.
이를 위해 중앙에 저장소를 두고 서로 부모-자식 관계 또는 연관관계를 가지지 않은 컴포넌트에서 상태에 접근 및 공유를 하기 위해 저장소 스토어(store)
를 사용한다.
import {writable, readable, derived, get} from 'svelte/store';
- svelete/store 모듈을 통해 스토어 구현이 가능하다.
writable 💡
import {writable} from 'svelte/store';
// store = writable(value: any)
// store = writable(value: any, (set: (value: any) => void) => () => void)
export const storeNum = writable(0);
// console.log(storeNum);
Object
set: ƒ (new_value)
update: ƒ update(fn)
subscribe: ƒ subscribe(run, invalidate = noop)
__proto__: Object
storeNum.set(9999); // 9999 로 값 변경
storeNum.update((value) => value + 100); // 9999 + 100;
- 스토어를 정의하면, 상태 객체를 반환합니다.
- set(), update(), subscribe() 메서드를 가지고 있습니다.
- writable() 에서 두번째 인자값으로 함수가 넘겨지면, 해당 상태객체가 .subscribe() 호출이 (0 -> 1) 되었을 때, 호출이 된다.
- set() 은 상태객체에 설정된 값을 지정합니다. ex) set(999); 라고 하면 999로 지정됩니다.
- update() 은 기존 상태객체값을 변경할때 사용합니다. 인자는 (value) => value 입니다.
import {writable} from 'svelte/store';
// store = writable(value: any, (set: (value: any) => void) => () => void)
export const storeNum2 = writable(100, (set) => {
console.log('got a subscriber!!'); // 해당 상태를 최초로 구독하면 호출
return () => console.log('더 이상 해당 상태를 구독하는 곳이 없음!!');
});
const noWatchStoreNum = storeNum2.subscribe((value) => {
// got a subscriber!! print
console.log(value); // 100
});
const noWatchStoreNum2 = storeNum2.subscribe((value) => {
console.log(value); // 100
});
noWatchStoreNum();
noWatchStoreNum2(); // print : 더 이상 해당 상태를 구독하는 곳이 없음!!
- subscribe() 메서드는 마치 vue 에 watch 속성 같이 동작을 합니다.
변경이 일어나면 통지를 해주는 느낌
- subscribe() 메서드로 해당 상태객체를 구독하면, 익명함수를 하나 반환 받습니다.
() => {
const index = subscribers.indexOf(subscriber);
if (index !== -1) {
subscribers.splice(index, 1);
}
if (subscribers.length === 0) {
stop();
stop = null;
}
};
import {writable} from 'svelte/store';
const name = writable('zero');
name.subscribe((value) => {
// 여기서, 해당 컴포넌트 변수에 할당을 해주면 되겠지요?
console.log(value);
});
name.set('zero86');
name.update((value) => `hello ${value}`);
// 결과(console.log())
// zero
// zero86
// hello zero86
readable 💡
- 스토어를 정의하면, 상태객체를 반환합니다.
- readable 은 이름에서도 알수있듯이 외부에서 변경할 수 없는 상태객체입니다.
import {writable, readable, derived, get} from 'svelte/store';
// store = readable(value: any, (set: (value: any) => void) => () => void)
const name = readable('zero86');
const age = readable(10, (set) => {
// set();
});
- subscribe() 메서드를 가지고 있습니다.
- 두번째 인자로 함수를 넘겨주고 해당 함수안에서 set() 으로 스토어 객체값을 변경할 수 있습니다.(이거마저 없으면 정말 값을 변경 불가능 + writable() 처럼 최초 구독이 발생하면 실행)
const name = readable('zero86', (set) => {
let flag = true;
const interval = setInterval(() => {
if(flag) {
set(`hi zero86`);
flag = !flag
} else {
set(`hello zero86`);
flag = !flag
}
}, 800);
return () => clearInterval(interval);
});
name.subscribe(value => {
console.log(value);
});
derived 💡
- 파생 스토어도 상태 객체를 반환합니다.
- subscribe() 메서드를 가지고 있습니다.
import {writable, derived} from 'svelte/store';
const count = writable(100);
// store = derived(a, callback: (a: any) => any)
// store = derived(a, callback: (a: any, set: (value: any) => void) => void | () => void, initial_value: any)
const doubleCount = derived(count, $count => $count * 2); // 200;
count.set(10); // 변경하면, dobuleCount 는 20이 됩니다.
- 하나 이상의 상태객체를 참조하여 값을 변경 또는 가공 후, 파생한 상태객체를 반환합니다.(참조한 상태객체에 종속성이 생기며 변경될때마다 파생한 상태객체를 반환합니다.)
- vue computed 속성처럼 동작을 합니다.
const count = writable(100);
const doubleCount = derived(count, $count => $count * 2); // 200;
const doubleCount2 = derived(count, ($count, set) => {
return $count * 2; // set 인자가 설정되면 더이상 이방식으로 작동하지 않음.
});
doubleCount2.subscribe(value => {
console.log(value); // undefined
});
- 콜백함수에 두번째 인자로 set이 지정되면, set() 을 호출하여 값을 지정해야 합니다.(위 코드를 수정해 볼까요?)
const count = writable(100);
const doubleCount2 = derived(count, ($count, set) => {
// return $count * 2; // set 인자가 설정되면 더이상 이방식으로 작동하지 않음.
set($count * 2);
});
doubleCount2.subscribe(value => {
console.log(value); // 200
});
- 만약, 하나의 상태객체 말고 여러개 상태객체를 파생하고 싶다면?
// store = derived([a, ...b], callback: ([a: any, ...b: any[]]) => any)
// store = derived([a, ...b], callback: ([a: any, ...b: any[]], set: (value: any) => void) => void | () => void, initial_value: any)
import {writable, readable, derived, get} from 'svelte/store';
const count = writable(100);
const count2 = writable(100);
const doubleCount = derived([count, count2], ([$count, $count2]) => $count + $count2); // 200;
doubleCount.subscribe(value => {
console.log(value);
});
get 💡
- 상태객체에서 상태값을 가지고 조회해옵니다.
- 간혹 상태객체가 subscribe() 되지 않았다면, get() 을 통해 조회를 해오면 됩니다.(하지만 문서에는 권장하지 않는다라고 되어있습니다)
import {writable, get} from 'svelte/store';
const count = writable(100);
const getCount = get(count);
console.log(getCount); // 100
- 조심해야할점은 get() 을 해올때 해당 상태객체 값이 변경이 동시에 이루어지면 가지고오는값을 보장받을수 없습니다.(변경되기전값인지, 변경후인지)
- 중간에 count 값을 바꾸면 getCount 에는 이전 100값이 그대로 존재하며 새로 get()을 해주는 로직을 작성해야 합니다.
이제 스토어 상태객체 값을 참조해보자!💡
<script>
import {writable} from 'svelte/store';
const count = writable(100);
let localCount = 0;
count.subscribe(value => {
localCount = value;
});
</script>
<div>
count : {localCount}
</div>
- 위 방식은 구독을 해서 해당 컴포넌트 상태변수에다가 할당하여 사용하는 방식이다.
<script>
import {writable} from 'svelte/store';
const count = writable(100);
</script>
<div>
count : {$count}
</div>
<button on:click={() => $count += 1}>
Plus 1
</button>
- subscribe()를 가지는 스토어 객체만 $를 사용해 참조(writable, readable, derived)
svelte로 취업하는 건가요..!