AtomEffects localStorage.

rifkin·2022년 5월 14일
4
post-thumbnail

-요구사항-
"즐겨찾기"된 데이터는 로컬에 저장하여, 다음에 접속 했을 때, 즐겨찾기 조회가 되어야 합니니다..??!?💢💥
개발 요건에 Recoil 사용 우대라는 말에 홀려 거의 모든 상태를 recoil로 떡칠해버린 죄였을까...
즐겨찾기 목록을 로컬에서 가져올때 문제가 생겨버렸다.

문제 인식하기

code-1

recoil state
// 즐겨찾기 리스트
const bookMarkList = atom<IMovie[]>({
  key: 'GripBookMark',
  default: [],
})

// 즐겨찾기|즐겨찾기 취소할 IMovie 객체
const pickMovie = atom<IMovie>({
  key: 'pickMovie',
  default: undefined,
})

// pickMovie가 즐겨찾기 리스트에 존재하면 false, 아니면 true 
const bookMarkButtonState = selector<boolean>({
  key: 'bookMarkButtonState',
  get: ({ get }) => {
    const booklist = get(bookMarkList)
    const pickedMoive = get(pickMovie)

    const result = booklist.filter(book => book.imdbID === pickedMoive.imdbID)
    if (result.length === 0)
      return true
    return false
  }
})
  1. 영화를 선택하면 즐겨찾기|즐겨찾기 취소를 통해 bookMarkList를 갱신한다.
  2. bookMarkList는 recoil값이므로 즐겨찾기 탭이 리렌더링 된다.
  3. bookMarkList값이 변할때마다 localStorage에 저장한다.
  4. 즐겨찾기 탭으로 전환할 때마다 localStorage값을 가져와 bookMarkList에 갱신한다.

즉 Modal component에서 값을 추가|삭제 하면 bookMarkList는 갱신되고 useEffect에서 localStorage에 갱신된 값을 저장한다.

  • code-2
useEffect(() => {
  // 해당 컴포넌트가 onMount와 리렌더링 될때마다.
  const readData = localStorage.getItem('GripBookMark') as string
  if(!readData){
    setBookMarkList([])
  }
  else{
    setBookMarkList(JSON.parse(readData))
  }
  // 해당 컴포넌트가 unMount 될 때마다.
  return () => {
    const setData = JSON.stringify(markList)
    localStorage.setItem('GripBookMark', setData)
  }
}, [bookMarkList])

뇌🧠: "값이 변경될 때마다 저장하고 가져올 수 있잖아?!"
React💻: "돼나 봐라ㅋㅋㅋㅋㅋㅋㅋㅋㅋ"

뇌🧠: "아 안됌?ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ"

원인.

새로고침(F5)을 누르거나 react를 다시 시작하면 localStorage에는 빈 배열만 저장된다.

문제의 원인은 code-1에 있다. 바로 recoil 값인 bookMarkList다.
React💻가 다시 시작되면서 default 값인 빈 배열이 로드되면서 localStorage에 저장되기 때문이다.

React💻: "수고해라 닝겐"
뇌🧠: "엉망이네..."

Connecting LocalStorage to Recoil.

뇌🧠: "그러면 recoil default에 localStorage 값을 넣으면 되는거 아님?"

맞다. default 값을 처음부터 localStorage 값으로 설정하면 된다. 그러면 code-2의 내용이 없어도 새로고침을 해도 default값이 localStorage이므로 잘 작동한다.

AtomEffects.

recoil 공식문서에 Local Storage 관련 글이 있어 이를 사용해보기로 했다.

아래의 코드는 공식문서에서 발최했다.

const localStorageEffect = key => ({setSelf, onSet}) => {
  const savedValue = localStorage.getItem(key)
  if (savedValue != null) {
    setSelf(JSON.parse(savedValue));
  }

  onSet((newValue, _, isReset) => {
    isReset
      ? localStorage.removeItem(key)
      : localStorage.setItem(key, JSON.stringify(newValue));
  });
};

const currentUserIDState = atom({
  key: 'CurrentUserID',
  default: 1,
  effects: [
    localStorageEffect('current_user'),
  ]
});

Typescript가 시급하다.....😂
여차저차 해서 아래처럼 수정을 했다.

const localStorageEffect: <T>(key: string) => AtomEffect<T> =
  (key: string) =>
    ({ setSelf, onSet }) => {
  const savedValue = localStorage.getItem(key)
  if (savedValue != null) {
    setSelf(JSON.parse(savedValue))
  }
  onSet((newValue, _, isReset) => {
    isReset
      ? localStorage.removeItem(key)
      : localStorage.setItem(key,JSON.stringify(newValue))
  })
}

const bookMarkList = atom<IMovie[]>({
  key: 'GripBookMark',
  default: [],
  effects: [
    localStorageEffect<IMovie[]>('GripBookMark')
  ]
})

타입을 바꾸고 다시 실행해 보았다.
결과는!!!!

effects_UNSTABLE

effectseffects_UNSTABLE로 바꿔주니 문제가 해결되었다. 빌드할 때 문제는 없지만 해당 문제는 공식해결 방식이 나오지 않은 상태이다.

이제 localStorage에 저장하거나 가져오기 위한 귀찮은 짓은 하지 않아도 된다.
즐겨찾기 값이 변경될 때마다 localStorage에 저장하고 그 값을 bookMarkList가 잘 읽는다.

다른 방법으로는 recoil-persist가 존재한다. 해당 라이브러리는 간단하게 사용할 수 있어 좋은 것 같다.

profile
Viva La Vida!

0개의 댓글