[POBTIL] 10일차

SWP·2022년 5월 13일
0

POBTIL

목록 보기
10/21
post-thumbnail

북마크

로직
1. item클릭시 confirm모달 => 이 때 북마크 여부에 따라 추가창/ 취소창
2. 모달창의 버튼 클릭시 리코일 및 스토리지 저장 / 해당 id 값 삭제
3. map돌린 Item컴포넌트에 현재 id가 있고 리코일엔 bookmark된 배열과 그안에 id가 있을것.=>리코일의 id배열.includes(itemId)이면 북마크 돼있는 isMarked = true로 바꿔줌
북마크 모달창
방법1 portal 및 props이용
movie아이템에 onClickModal 이벤트 달고 props로 모달에 isOpenModal, item, isMarked 을 props로 넘겨줌

발견된에러

북마크 추가 제거시 setIsMarked를 따로 props로 넘겨줘야 제대로 동작하는 문제

// MovieItem.js
  useEffect(() => {
    // console.log(bookmarkedList, '체크',isMarked)
    bookmarkedList.map((markedItem) => {
      if (markedItem.imdbID === item.imdbID) setIsMarked(true)
      else setIsMarked(false) // --1
    })
   // console.log(22)
  }, [bookmarkedList, isMarked, item.imdbID])

// BookmarkModal.js
  const handleAddMarkBtn = () => {
    // setIsMarked(true) // ---2
    setBookmarkedList(bookmarkedList.concat(item))
    setIsOpenModal(false)
    store.set('bookmarkMovieList', bookmarkedList.concat(item))
  }
  const handleDeleteMarkBtn = () => {
    // setIsMarked(false) // ---3 
    setBookmarkedList(bookmarkedList.filter((prev) => prev.imdbID !== item.imdbID))
    setIsOpenModal(false)
    store.set(
      'bookmarkMovieList',
      bookmarkedList.filter((prev) => prev.imdbID !== item.imdbID)
    )
  }
  .
  .
  .
  <BookmarkIcon className={isMarked ? styles.marked : styles.noMarked} />

북마크를 구현하는 과정에서 리코일의 atom으로 북마크된 리스트를 관리하고 있었고, 이를 모달창에서 변경시켜줬을 때, 이를 구독하는 MovieItem 컴포넌트에서 useEffect의 디펜던시에 해당 리스트를 넣고, 그 때마다 isMarked의 상태를 변경시켜 재렌더링으로 북마크 아이콘의 색을 변경시켜주려 했다.

(아톰의 상태가 변경되어도 재렌더링이 구독하는 컴포넌트의 재렌더링이 안되는거였나 해서 공식문서 다시 봄)

처음에는 처음에 넣지 않았었는데, 예상하다시피, 취소 때 원하는 대로 동작이 안됐다. 이 때 주석처리된 2,3을 넣으면 동작을 했다. setIsMarked를 props를 통해 넘겨주면 되는걸 보니 기본 정해놓은 로직에는 이상이 없음을 확인!
이에 각 과정에 console.log를 찍어봄으로써 useEffect가 재실행되는지, 아톰의 값이 그때마다 변경되고 있는지 체크했고, 문제가 없었다. isMarked에 따라 아이콘의 className을 따로 줬는데, 만약 true였던 컴포넌트일 경우 false로 변경시켜주는 로직이 없어 className이 변경되지않음을 발견!
해당 로직을 추가시켜줌으로써 props로 불필요하게 setIsMarked를 넘기는 것을 없애줬다.

이렇게 하니 추가하면 다른게 지워지고 하는 에러가 생김

다른사람깃헙을 구경하다 내것보다 좋아보이는 코드를 발견... 훔치자!

기존

  const [isMarked, setIsMarked] = useState(false)
  const [bookmarkedList] = useRecoilState(bookmarkMovieList)

  useEffect(() => {
    bookmarkedList.map((markedItem) => {
      if (markedItem.imdbID === item.imdbID) setIsMarked(true)
    })
  }, [bookmarkedList, isMarked, item.imdbID])

상태를 두개를 쓰고, 그거에 따라 useEffect로 isMarked를 구현했는데

const [bookmarkedList] = useRecoilState(bookmarkMovieList)
const isMarked = !!bookmarkedList.filter((markedItem) => markedItem.imdbID === item.imdbID).length

위의 7줄짜리 코드를 두 줄로 줄였다.
또 이렇게하니 리코일상태가 변경될 때 재렌더링되면서 isMarked를 부여하니 위에 문제도 해결. 참고로 js에서 빈배열의 boolean은 true라서 .length를 붙여줬는데 이건 좀 맘에 안든다... => filter대신 find를 이용하니 length 빼줄 수 있음.

리팩토링

1

기존

변경

기존에는 delete나 add함수에서 뷰와 관련된 모달을 꺼주고 켜주고까지 처리했는데, btn click 함수를 분리시켜줌으로써 뷰와 리스트에 추가하는 로직함수를 분리시켜줘서 더 좋은 코드라고 생각했다.

2

기존

  const addBookmark = () => {
    // 아래코드는 recoil의 setState가 비동기여서 가능한 코드
    setBookmarkedList((prevList) => prevList.concat(item))
    store.set('bookmarkMovieList', bookmarkedList.concat(item))
  }

  const deleteBookmark = () => {
    // 아래 코드는 bookmarkedList 현재 비동기로 스토어로 저장할 때 bookmarkedList가 바뀐지 안바뀐지 체크가 안되지만 어차피 filter 처리되는 코드
    // 다른 방법으로는 위에서 새 리스트를 위에서 만들고 같은 리스트를 넣어주는 방식이 있음.
    setBookmarkedList((prevList) => prevList.filter((prev) => prev.imdbID !== item.imdbID))
    store.set(
      'bookmarkMovieList',
      bookmarkedList.filter((prev) => prev.imdbID !== item.imdbID)
    )
  }
  

변경

 const addBookmark = () => {
    const newList = bookmarkedList.concat(item)
    setBookmarkedList(newList)
    store.set('bookmarkMovieList', newList)
  }

  const deleteBookmark = () => {
    const newList = bookmarkedList.filter((prev) => prev.imdbID !== item.imdbID)
    setBookmarkedList(newList)
    store.set('bookmarkMovieList', newList)
  }

처음에는 리코일 setState 후 store 저장할 때, 비동기로 concat을 해줬는데, concat이 두번 반복될뿐더러 비동기적인 코드를 이용하므로 예측이 더 어려우므로, 변경한 코드가 더 낫다고 생각했다.

profile
잘하고싶다...

0개의 댓글