๐Ÿ‘ฉโ€๐Ÿ’ป
Megaptera Frontend
  • ์ฃผ์ฐจ๋ณ„ ํ•™์Šต
    • megaptera-front
    • 1. ๊ฐœ๋ฐœ ํ™˜๊ฒฝ
      • 1. ๊ฐœ๋ฐœ ํ™˜๊ฒฝ
      • 2. TypeScript
      • 3. React
      • 4. Testing Library
      • 5. Parcel & ESLint
      • ํ•œ ์ฃผ๋ฅผ ๋งˆ์น˜๋ฉฐ
    • 2. JSX
      • 1. JSX
      • ํ•œ ์ฃผ๋ฅผ ๋งˆ์น˜๋ฉฐ
    • 3. React๋กœ ์‚ฌ๊ณ ํ•˜๊ธฐ
      • 1. React Component
      • 2. React State
      • ํ•œ ์ฃผ๋ฅผ ๋งˆ์น˜๋ฉฐ
    • 4. React Hooks
      • 1. Express
      • 2. Fetch API & CORS
      • 3. React์˜ Hook
      • 4. useRef & Custom Hook
      • 5. usehooks-ts
      • ํ•œ ์ฃผ๋ฅผ ๋งˆ์น˜๋ฉฐ
    • 5. ํ…Œ์ŠคํŠธ
      • 1. TDD
      • 2. React Testing Library
      • 3. MSW
      • 4. Playwright
      • ํ•œ ์ฃผ๋ฅผ ๋งˆ์น˜๋ฉฐ
    • 6. External Store
      • 1. External Store
      • 2. TSyringe
      • 3. Redux ๋”ฐ๋ผํ•˜๊ธฐ
      • 4. usestore-ts
      • ํ•œ ์ฃผ๋ฅผ ๋งˆ์น˜๋ฉฐ
    • 7. React Router
      • 1. Routing
      • 2. Routes
      • 3. Router
      • 4. Navigation
      • ํ•œ ์ฃผ๋ฅผ ๋งˆ์น˜๋ฉฐ
    • 8. CSS in JS
      • 1. Design System
      • 2. Style Basics
      • 3. CSS in JS
      • 4. styled-components
      • 5. props์™€ attrs
      • 6. Global Style & Theme
      • ํ•œ ์ฃผ๋ฅผ ๋งˆ์น˜๋ฉฐ
    • 9. ์‡ผํ•‘๋ชฐ ๋ชฉ๋ก, ์ƒํ’ˆ ํŽ˜์ด์ง€
      • 1. ๊ฐœ๋ฐœํ•˜๊ธฐ ์ „ ์ค€๋น„
      • 2. ๋ชฉ๋ก ๋ณด๊ธฐ
      • 3. ์ƒํ’ˆ ์ƒ์„ธ ๋ณด๊ธฐ
      • 4. ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๋ณด๊ธฐ
      • 5. ์žฅ๋ฐ”๊ตฌ๋‹ˆ์— ์ƒํ’ˆ ๋‹ด๊ธฐ
      • ํ•œ ์ฃผ๋ฅผ ๋งˆ์น˜๋ฉฐ
    • 10. ์‚ฌ์šฉ์ž ์ธ์ฆ, ์ธ๊ฐ€
      • 1. ๋กœ๊ทธ์ธ
      • 2. ๋กœ๊ทธ์•„์›ƒ
      • 3. ํšŒ์›๊ฐ€์ž…
      • 4. ์ฃผ๋ฌธ ๋ชฉ๋ก & ์ฃผ๋ฌธ ์ƒ์„ธ
      • ํ•œ ์ฃผ๋ฅผ ๋งˆ์น˜๋ฉฐ
    • 11. ์ฃผ๋ฌธ, ๊ฒฐ์ œ
      • 1. ๋ฐฐ์†ก ์ •๋ณด ์ž…๋ ฅ
      • 2. ํฌํŠธ์› ๊ฒฐ์ œ ์š”์ฒญ
      • 3. ๋ฐฐ์†ก ๋ฐ ๊ฒฐ์ œ ์ •๋ณด ์ „๋‹ฌ
      • ํ•œ ์ฃผ๋ฅผ ๋งˆ์น˜๋ฉฐ
    • 12. ์–ด๋“œ๋ฏผ
      • 1. ๊ด€๋ฆฌ์ž ์›น ์‚ฌ์ดํŠธ ๊ฐœ๋ฐœ ์‹œ์ž‘
      • 2. ๋กœ๊ทธ์ธ, ์‚ฌ์šฉ์ž ๋ชฉ๋ก
      • 3. ์นดํ…Œ๊ณ ๋ฆฌ ๊ด€๋ฆฌ
      • 4. ์ฃผ๋ฌธ ๊ด€๋ฆฌ
      • 5. ์ƒํ’ˆ ๊ด€๋ฆฌ
      • ํ•œ ์ฃผ๋ฅผ ๋งˆ์น˜๋ฉฐ
Powered by GitBook
On this page
  • 1. Action์„ ๋ฉ”์†Œ๋“œ๋กœ ์ฒ˜๋ฆฌํ•˜๊ธฐ
  • CounterStore, useCounterStore ๋งŒ๋“ค๊ธฐ
  • 2. usestore-ts
  • ์‚ฌ์šฉ ๋ฐฉ๋ฒ•
  • Immer
  • 3. useSyncExternalStore
  1. ์ฃผ์ฐจ๋ณ„ ํ•™์Šต
  2. 6. External Store

4. usestore-ts

Previous3. Redux ๋”ฐ๋ผํ•˜๊ธฐNextํ•œ ์ฃผ๋ฅผ ๋งˆ์น˜๋ฉฐ

Last updated 2 years ago

1. Action์„ ๋ฉ”์†Œ๋“œ๋กœ ์ฒ˜๋ฆฌํ•˜๊ธฐ

๐Ÿ’ก Redux์™€ ๋‹ฌ๋ฆฌ ๋‹จ์ผ ์Šคํ† ์–ด๊ฐ€ ์•„๋‹ˆ๊ธฐ์—, ์Šคํ† ์–ด ์—ฌ๋Ÿฌ ๊ฐœ๋ฅผ ๋งŒ๋“ค์–ด์„œ ์‚ฌ์šฉํ•˜๋ฉด ๋จ

CounterStore, useCounterStore ๋งŒ๋“ค๊ธฐ

// src/stores/ObjectStore.ts

type Listener = () => void;

export default class ObjectStore {
    private listeners = new Set<Listener>();

    addListener(listener: Listener) {
        this.listeners.add(listener);
    }

    removeListener(listener: Listener) {
        this.listeners.delete(listener);
    }

    protected publish() {
        this.listeners.forEach((listener) => listener());
    }
}
// src/stores/CounterStore.ts

import {singleton} from 'tsyringe';

import ObjectStore from './ObjectStore';

@singleton()
export default class CounterStore extends ObjectStore {
    count = 0;

    /**
     * ์นด์šดํŠธ๋ฅผ ์ฆ๊ฐ€์‹œํ‚จ๋‹ค.
     */
    increase(step = 1) {
        this.count += step;
        this.publish();
    }

    /**
     * ์นด์šดํŠธ๋ฅผ ๊ฐ์†Œ์‹œํ‚จ๋‹ค.
     */
    decrease() {
        this.count -= 1;
        this.publish();
    }
}
// src/hooks/useObjectStore.ts

import {useEffect} from 'react';

import useForceUpdate from './useForceUpdate';

import ObjectStore from '../stores/ObjectStore';

export default function useObjectStore<T extends ObjectStore>(store: T): T {
    const forceUpdate = useForceUpdate();

    useEffect(() => {
        store.addListener(forceUpdate);

        return () => store.removeListener(forceUpdate);
    }, [store, forceUpdate]);

    return store;
}
// src/hooks/useCounterStore.ts 

import {container} from 'tsyringe';

import useObjectStore from './useObjectStore';

import CounterStore from '../stores/CounterStore';

export default function useCounterStore() {
    const store = container.resolve(CounterStore);

    return useObjectStore(store);
}

2. usestore-ts

React state management library

๐Ÿ’ก ๋‚ด๋ถ€์ ์œผ๋กœ ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š”์ง€ ์ดํ•ดํ•˜๊ณ  ์‚ฌ์šฉํ•  ๊ฒƒ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ž˜ ๋ชจ๋ฅธ๋‹ค๋ฉด ์–ด๋ ค์šธ ์ˆ˜ ์žˆ์Œ ๋‚จ์—๊ฒŒ ์„ค๋ช…ํ•  ๋•Œ ๋‚ด๋ถ€์ ์œผ๋กœ๋Š” ์ด๋Ÿฐ ๋™์ž‘์„ ํ•˜๊ณ  ์žˆ์œผ๋ฉฐ ์–ด๋–ค ๋ฐฉ์‹์œผ๋กœ ์ž‘๋™ํ•˜๋Š”์ง€ ์–˜๊ธฐํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•จ

์‚ฌ์šฉ ๋ฐฉ๋ฒ•

ํŒจํ‚ค์ง€ ์„ค์น˜

npm install usestore-ts

tsconfig.json ํŒŒ์ผ decorators ์‚ฌ์šฉ ์˜ต์…˜ ๋ณ€๊ฒฝ

    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,

Store ์ƒ์„ฑ

// CounterStore.ts

import {singleton} from 'tsyringe';

import {Store, Action} from 'usestore-ts';

@singleton()
@Store()
export default class CounterStore {
    count = 0;

    @Action()
    increase(step = 1) {
        this.count += step;
    }

    @Action()
    decrease() {
        this.count -= 1;
    }
}

Custom Hook ์ž‘์„ฑ

// 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 [{ count }, store] = useStore(counterStore);

์ฒซ๋ฒˆ์งธ ์ธ์ž๋กœ ์Šคํ† ์–ด์˜ ๊ฐ’, ๋‘๋ฒˆ์งธ ์ธ์ž๋กœ ์Šคํ† ์–ด ์ž์ฒด๊ฐ€ ๋ฐ˜ํ™˜

๋ถˆ๋Ÿฌ์™€์„œ ์‚ฌ์šฉํ•˜๊ธฐ

// Counter.tsx

export default function Counter() {
    const [{count}] = useCounterStore();
    // ๋‚ด์šฉ
}
// CounterControl.tsx

export default function CounterControl() {
	const [, store] = useCounterStore();
    // ๋‚ด์šฉ
}

๋น„๋™๊ธฐ ํ•จ์ˆ˜์— @Action ์ถ”๊ฐ€

โš ๏ธ ๋น„๋™๊ธฐ ํ•จ์ˆ˜์— @Action์„ ๋ถ™์ด๋ฉด ๋‹ค๋ฅด๊ฒŒ ์ž‘๋™ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์— ์ฃผ์˜ ๋ณ„๋„์˜ ์•ก์…˜์„ ๋งŒ๋“ค๋ฉด ์‹ ๊ฒฝ ์“ธ ๋ถ€๋ถ„์ด ์ค„์–ด๋“ฆ

  • start, complete ๋ถ™์€ ๊ฒƒ์ด ์•ก์…˜

@singleton()
@Store()
class PostStore {
    posts: Post[] = [];

    // ์—ฌ๊ธฐ์— @Action() ๋ฅผ ๋ถ™์ผ ๊ฒฝ์šฐ ํ”„๋กœ๋ฏธ์Šค๋ฅผ ๋ฆฌํ„ดํ•ด์„œ publish
    async fetchPosts() {
        this.startLoading();

        const posts = await fetchPosts();

        this.completeLoading(posts);
    }

    @Action()
    startLoading() {
        this.posts = [];
    }

    @Action()
    completeLoading(posts: Post[]) {
        this.posts = posts;
    }
}

Immer

import produce from "immer"

const nextState = produce(baseState, draft => {
    draft[1].done = true
    draft.push({title: "Tweet about it"})
})

3. useSyncExternalStore

external store๋ฅผ ๊ตฌ๋…ํ•  ์ˆ˜ ์žˆ๋Š” React Hook

const snapshot = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot ?)

๋ถˆ๋ณ€์„ฑ์„ ์œ ์ง€ํ•ด์ฃผ๋Š” ์ฝ”๋“œ๋ฅผ ํŽธ๋ฆฌํ•œ ๋ฐฉ์‹์œผ๋กœ ์ž‘์—…ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์˜ค๋ธŒ์ ํŠธ ์ „์ฒด๋ฅผ ๋ณต์‚ฌํ•ด์„œ ์ฃผ๊ณ , ๊ทธ๊ฒƒ์„ ์ด์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋˜์–ด ์žˆ์Œ ์—๋„ ๊ธฐ๋ณธ์œผ๋กœ ์ ์šฉ

๐Ÿ”— ์‹ค์Šต ๋งํฌ
usestore-ts
๐Ÿ”— ์‹ค์Šต ๋งํฌ
Immer
Redux Toolkit
useSyncExternalStore
FECONF 2022 - ์ƒํƒœ๊ด€๋ฆฌ ์ด ์ „์Ÿ์„ ๋๋‚ด๋Ÿฌ ์™”๋‹ค