4. usestore-ts

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

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

Immer

불변성을 유지해주는 코드를 편리한 방식으로 작업할 수 있게 해주는 라이브러리 오브젝트 전체를 복사해서 주고, 그것을 이용할 수 있도록 되어 있음 Redux Toolkit 에도 기본으로 적용

import produce from "immer"

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

3. useSyncExternalStore

useSyncExternalStore FECONF 2022 - 상태관리 이 전쟁을 끝내러 왔다

external store를 구독할 수 있는 React Hook

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

Last updated