비동기 api 코드는 react-query를 사용하여 처리하다보니 이제는 굳이 redux saga를 쓸 필요성이 없어지게 되었다. 그래서 보다 간편하게 사용할 수 있고 atom effects를 활용하여 쉽게 localStorage에서 상태를 유지할 수 있는 점에서 recoil을 선택하게 되었다.
우선, 기존의 토글을 사용하여 테마를 바꾸는 코드를 redux → recoil로 변경해볼 예정이다.
기존의 코드는 아래와 같이 작성되어 있으며 localstorage와 해당 상태를 동기화 해주는 코드를 추가적으로 작성해야 했다.
const SET_MODE = 'common/SET_MODE' as const;
export const setMode = createAction(SET_MODE);
const initialState = {
mode: getInitTheme(),
};
const common = handleActions(
{
[SET_MODE]: (state) => {
setItem('theme', !state.mode); // localstorage 관리하는 코드
return {
...state,
mode: !state.mode,
};
},
},
initialState,
);
export default common;
recoil로 작성하게 되면 아래 코드만 작성해주고 루트 컴포넌트에 RecoilRoot만 추가해주면 된다.
export const themeState = atom({
key: 'themeState',
default: true,
});
여기서 해당 상태를 localStorage와 같이 사용하기위해서는 조금 더 코드를 작성해주면 된다. setSelf 함수는 로컬스토리지에서 값을 불러와 초기값을 지정해주는 함수이고 onSet함수가 해당 atom값이 변경될 때마다 로컬스토리지와 atom 값을 동기화해주는 역할을 한다.
const localStorageEffect =
<T>(key: string) =>
({ setSelf, onSet }: any) => {
const savedValue = getItem(key);
// Callbacks to set or reset the value of the atom.
if (savedValue != null) {
setSelf(savedValue);
}
// Subscribe to changes in the atom value.
onSet((newValue: T, _: any, isReset: boolean): void => {
isReset ? removeState(key) : setItem(key, newValue);
});
};
이제 기존의 atom에 위에서 작성한 함수를 effects에 추가해주기만 하면 된다.
export const themeState = atom({
key: 'themeState',
default: true,
effects: [localStorageEffect(KEY)],
});
나머지 전역상태를 나타낼 코드도 설정하는 부분이 거의 비슷해서 크게 차이 없이 바꿀 수 있었다.
음... recoil을 사용하면서 느꼈던 장점은 사용하기 위해 작성해야 할 코드가 redux에 비해 정말 적었다는 점과 context와 비교하면 렌더링 최적화에 대한 고민이 적어졌다는 점이다.
끝~