ํ์ตํค์๋
์๋์จ์ผ์์ ์ ์ํ React state management library
๊ฐ๋จํ, ํ์ง๋ง TypeScript ์ ์ฉ์ธ, React ์ํ ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ usestore-ts ์
๋๋ค. React ์ธ๋ถ(์ ํํ๋ UI ๋ ์ด์ด๊ฐ ์๋ ์ฝ์ด!)๋ฅผ ๋ด๊ฐ ์ํ๋ ๋ฐฉ์(FP or OOP)์ผ๋ก ๊ตฌ์ฑํ๊ณ Flux์ฒ๋ผ ๋จ๋ฐฉํฅ ํ๋ฆ์ผ๋ก ์ฎ์ด๋ด๋ ๋์ฆ์ ๋ง์ถฐ์ง ๋๊ตฌ์
๋๋ค.
๐ค usesotre-ts ์ค์น ๋ฐ ์ค์
usesotre-ts ์ค์น
npm install usestore-ts
Typescript @ ๋ฐ์ฝ๋ ์ดํฐ๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์ tsconfig.json
ํ์ผ ์์ฑ ์ถ๊ฐ
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
๐ฉ๐ปโ๐ป ๊ฐ๋จํ ์์ ๋ฅผ ํตํด ์ค์ต
๊ธฐ์กด์ TSyringe + reflect-metadata๋ฅผ ์ฌ์ฉํ์ฌ ๋ง๋ ๊ฐ๋จํ ์คํ ์ด๋ฅผ usestore-ts ๋ฅผ ์ฌ์ฉํด๋ณด๊ธฐ
Store ์์ฑ
usestore-ts์์ ์ ๊ณตํ๋ Store ๋ฐ์ฝ๋ ์ดํฐ๋ ๊ธฐ์กด ObjectStore ํด๋์ค๋ฅผ ๋์ฒดํ๋ค. ์ฆ, ObjectStore๊ฐ ์ ๊ณตํ๋ ๊ธฐ๋ฅ (๊ตฌ๋
๊ด๋ฆฌ, ์ํ๋ณ๊ฒฝ์ ์๋ฆฌ๋ ์์
๋ฑ)์ ๋ด๊ณ ์๋ค๋ ๊ฒ
// CounterStore.ts
import { singleton } from 'tsyringe';
import { Store, Action } from 'usestore-ts';
/*
* ์ฑ๊ธํค ๋ฐ์ฝ๋ ์ดํฐ๋ฅผ ์คํ ์ด ๋ฐ์ฝ๋ ์ดํฐ ๋ณด๋ค ์์์ ๋์.
* ์ฑ๊ธํค์ ๋จน์ ํด๋์ค๋ฅผ ์คํ ์ด ๋ฐ์ฝ๋ ์ดํฐ๋ฅผ ์ฌ์ฉํ๋ฉด ์ด์๊ฐ ์์ ์ ์์
*/
@singleton()
@Store()
export default class CounterStore {
count = 0;
// ์ก์
๋ฐ์ฝ๋ ์ดํฐ๋ publish ๋ฉ์๋๋ฅผ ๋จธ๊ธ๊ณ ์๋ค.
@Action()
increase(step = 1) {
this.count += step;
}
@Action()
decrease(step = 1) {
this.count -= step;
}
}
Custom Hook ์์ฑ
usestore-ts๊ฐ ์ ๊ณตํ๋ useStore ํ
์ผ๋ก ๊ธฐ์กด useObjectStore๋ฅผ ๋์ฒด forceUpdate ๋ฆฌ์ค๋๋ฅผ ๋จธ๊ธ๊ณ ์๋ค.
// useCounterStore.ts
import {container} from 'tsyringe';
import {useStore} from 'usestore-ts';
import CounterStore from '../stores/CounterStore';
export default function useCounterStore() {
const store = container.resolve(CounterStore);
return useStore(store);
}
const [ state , store ] = useStore(counterStore) ๋ฐํ๊ฐ ์ฌ์ฉํ๊ธฐ
import useCounterStore from '../hooks/useCounterStore';
export default function Counter() {
const [{ count }] = useCounterStore(); // ์ํ๊ฐ๋ง ์ฌ์ฉ
return (
<div>
<p>{`count: ${count}`}</p>
</div>
);
}
// CounterControl.tsx
import useCounterStore from '../hooks/useCounterStore';
export default function CounterController() {
const [, store] = useCounterStore(); // ์คํ ์ด์ Action๋ง ์ฌ์ฉ
const handleClickIncrease = (step?: number) => () => {
store.increase(step);
};
const handleClickDecrease = (step?: number) => () => {
store.decrease(step);
};
return (
<div>
<button type="button" onClick={handleClickIncrease(10)}>
Increase 10
</button>
<button type="button" onClick={handleClickIncrease()}>
Increase
</button>
<button type="button" onClick={handleClickDecrease(10)}>
Decrease 10
</button>
<button type="button" onClick={handleClickDecrease()}>
Decrease
</button>
</div>
);
}
โ ๏ธ ์ฌ์ฉ์ ์ฃผ์ ์ฌํญ
๋น๋๊ธฐ ํจ์์ @Action์ ๋ถ์ด๋ฉด ๋ค๋ฅด๊ฒ ์๋ํ ์ ์๋ค๋ ์ ์ ์ฃผ์! ๋ณ๋์ ์ก์
์ ๋ง๋ค๋ฉด ์ ๊ฒฝ ์ธ ๋ถ๋ถ์ด ์ค์ด๋ ๋ค.
@singleton()
@Store()
class PostStore {
posts: Post[] = [];
//@Action() ๐๐ป ์ด๋ฐ์์ผ๋ก ์ฌ์ฉํ๋ฉด ๋ค๋ฅด๊ฒ ๋์ ํ ์ ์๋ค?
async fetchPosts() {
this.startLoading();
const posts = await fetchPosts();
this.completeLoading(posts);
}
@Action()
startLoading() {
this.posts = [];
}
@Action()
completeLoading(posts: Post[]) {
this.posts = posts;
}
}
๐ ์ฐธ๊ณ