[React] 전역 상태 관리 라이브러리 제작기 (1)
전역 상태 관리 라이브러리 제작기 (1)
이전부터 '전역 상태 관리 라이브러리를 만들어보고 싶다'라는 생각을 갖고 있었습니다. 최근 Context API
와 Redux
의 구조가 어떻게 이뤄져 있는지에 대해 알아본 뒤 전역 상태를 관리하는 흐름에 대해 대충 알게 되어서 이를 활용해 라이브러리를 제작해보려 합니다.
라이브러리 설계
Context API
를 사용하지 않고 상태 관리를 하도록 작성할 예정입니다. 또한 store
가 생성될 때 store
에서 데이터를 갖고 있도록 하여 Provider
없이 사용 가능하도록 하려 합니다.
Provider
가 없는 상태 관리에 대해서는 zustand 상태 관리 라이브러리를 보고 아이디어를 얻었습니다.
필요한 요소
상태 관리 라이브러리를 만든다고 할 때, 제대로 동작하기 위해서는 다음과 같은 기능이 필요합니다.
- 현재 값을 얻을 수 있어야 함.
- 값을 업데이트 할 수 있어야 함.
- 값이 변경될 때 반응할 수 있어야 함.
const store = createStore(0);
store.getValue(); => 0
store.setValue(1);
store.getValue(); => 1
간단히 위와 같이 동작할 수 있어야 합니다.
리액트에 대응
React
에서는 state
또는 props
가 변경되어야 리렌더링이 발생하므로 위 작업을 리액트에서 반응할 수 있도록 해야합니다.
리렌더링을 하는 가장 쉬운 방법은 state
로 값을 관리하는 것입니다.
const store = createStore(0);
const Counter = () => {
const [value, setValue] = React.useState(store.getValue());
React.useEffect(() => {
const clean = store.onChange((nextValue) => {
setValue(nextValue);
});
// 해당 컴포넌트가 더이상 사용되지 않는다면
// `setValue` 작업 또한 일어나지 않아도 되므로 정리합니다.
return clean;
}, []);
};
새로운 컴포넌트가 생성될 때마다 위 코드를 가져다 사용하는 것은 매우 비효율적이기 때문에 Custom Hook을 만들어 위 기능을 분리하도록 합니다.
const useGlobalStore = (store) => {
const [value, setValue] = React.useState(store.getValue());
React.useEffect(() => {
const clean = store.onChange((nextValue) => {
setValue(nextValue);
});
// 해당 컴포넌트가 더이상 사용되지 않는다면
// `setValue` 작업 또한 일어나지 않아도 되므로 정리합니다.
return clean;
}, [store]);
return [value, store.setValue];
};
위 hook을 다음과 같이 사용할 수 있습니다.
const store = createStore(0);
const Counter = () => {
const [value] = useGlobalStore(store);
return <p>{value}</p>
};
const CounterButton = () => {
const [val, setVal] = useGlobalStore(store);
return <button onClick={() => setVal(val + 1)}>+1</button>
};
개선이 필요한 부분도 보이지만, 위와 같이 중복되는 로직을 분리할 수 있습니다.
useGlobalStore
는 useState
와 유사합니다. 동일한 점으로는 값과 값을 수정할 수 있는 설정 함수를 반환한다는 것이며, 차이점은 useState
는 로컬 상태를 생성하지만, useGlobalStore
는 전역에서 관리되는 즉, 값이 공유되는 상태를 생성한다는 것입니다.
createStore function
어떻게 사용되면 좋을지 알아봤으니 store
을 생성하는 createStore
함수에 대한 구현이 필요합니다.
우선, createStore의 반환 값은 다음과 같습니다.
type SetValueFunction<T = any> = (currValue: T) => T;
export interface CreateStoreReturnValue<T> {
getValue: () => T;
setValue: (nextValue: T) => void;
onChange: (callback: (newValue: T) => void) => void;
}
실제 구현은 다음과 같이 될 것입니다.
const createStore = <T = any>(defaultValue: T): CreateStoreReturnValue<T> = {
let value = defaultValue;
const callbackList: Array<(newValue: T) => void> = [];
const getValue = () => value;
const setValue = (newValue: T) => {
value = newValue;
callbackList.forEach((callback) => callback(newValue));
}
// value 값이 변경될 때 실행되는 callbackList를 관리합니다.
const onChange = (callback: (newValue: any) => void) => {
callbackList.push(callback);
// 추가된 callback을 제거하는 함수를 반환합니다.
return () => {
const idx = callbackList.findIndex(callback);
callbackList.splice(idx, 1);
};
};
return { getValue, setValue, onChange };
};
'Project > JavaScript Project' 카테고리의 다른 글
sagen - 상태 관리 라이브러리 (0) | 2021.02.10 |
---|---|
[React] 전역 상태 관리 라이브러리 제작기 (3) (0) | 2020.12.02 |
[React] 전역 상태 관리 라이브러리 제작기 (2) (0) | 2020.12.01 |
댓글
이 글 공유하기
다른 글
-
sagen - 상태 관리 라이브러리
sagen - 상태 관리 라이브러리
2021.02.10 -
[React] 전역 상태 관리 라이브러리 제작기 (3)
[React] 전역 상태 관리 라이브러리 제작기 (3)
2020.12.02 -
[React] 전역 상태 관리 라이브러리 제작기 (2)
[React] 전역 상태 관리 라이브러리 제작기 (2)
2020.12.01