module state

리액트에서 모듈 상태를 전역 상태로 사용하는 방법

모듈 상태(module state) 살펴보기

모듈 상태는 모듈 수준에서 정의된 변수다. 함수 외부에서 정의된 변수를 모듈 상태라고 가정하고 예시 코드를 살펴보자.

let state = {
    count: 0;
}

해당 객체가 모듈 상태라고 할 때, 객체에 접근하고 상태를 변경하는 함수를 정의해보자.

export const getState = () => state

export const setState = (newState) => {
    state = newState
}

함수를 사용해 상태를 갱신할 수 있게 setState를 수정할 수 있다.

export const setState = (newState) => {
    state = typeof nextState === 'function' ? newState(state) : newState

setState((state) => {
    return {
        ...state,
        count: state.count + 1
    }
})
}

모듈 상태를 직접 정의하지 않고, 상태와 상태에 접근할 수 있는 함수가 내부에 있는 컨테이너를 만들 수 있다.

export const createContainer = (initialState) =>
{
    let state = initialState
    const getState = () => state
    const setState = (newState) => {
        state = typeof newState === 'function' ? newState(state) : newState
    }
    return { getState, setState }

}

컨테이너는 다음과 같이 사용할 수 있다.

const { getState, setState } = createContainer({ count: 0 })

리액트에서 모듈 상태 사용하기

구독으로 모듈 상태 구현하는 에제

type Store<T> = {
    getState: () => T;
    setState: (action: T | ((prev: T) => T)) => void;
    subscribe: (callback: () => void) => void;
}

const createStore = <T extends unknown>(
    initialState: T
): Store<T> => {
    let state = initialState
    const callbacks = new Set<() => void>();
    const getState = () => state
    const setState = (nextState: T | ((prev: T) => T))=> {
        state = typeof nextState === 'function' ? (nextState as (prev: T) => T)(state) : nextState;
        callbacks.forEach((callback) => callback())
    }
    const subscribe = (callback: () => void) => {
        callbacks.add(callback)
        return () => {
            callbacks.delete(callback)
        }
    }

    return { getState, setState, subscribe}
}

createStore 사용하기

import { createStore } from './store'

const store = createStore({ count: 0 })
store.setState({ count: 1})
store.subscribe(...)

store의 상태 값과 갱신 함수를 튜플로 반환하는 사용자 정의 훅을 만들 수 있다.

const useStore = (store) => {
    const [state, setState] = useState(store.getState());

    useEffect(() => {
        const unsubscribe = store.subscribe(() => {
            setState(store.getState())
        });
        setState(store.getState())
        return unsubscribe
    }. [store])

    return [state, store.setState]
}

useStore 사용하기

const [state, setState] = useStore(store)

const inc = () => {
    setState((prev) => ({
        ...prev,
        count: prev.count + 1
    }))
}

return (
    <div>
        <h1>{state.count}</h1>
        <button onClick={inc}>Increment</button>
    </div>
)

Last updated