React Recoil 비동기 관련 'Can't perform a React state update on a component that hasn't mounted yet' 경고 해결하기

백민혁·2024년 1월 31일
1

개인프로젝트

목록 보기
4/7
post-custom-banner

recoil을 사용해서 게시물 리스트를 불러오는 작업도중 에러를 발견하였습니다.
코드를 한번 보면서 생각해보니 뭔가 비동기 처리하는 과정중 에러라고 생각하여 에러내용을 분석하였습니다.

번역

  • 아직 마운트되지 않은 구성 요소에서는 React 상태 업데이트를 수행할 수 없습니다. 이는 나중에 비동기적으로 호출하여 구성 요소를 업데이트하려고 시도하는 렌더링 함수에 부작용이 있음을 나타냅니다. 대신 이 작업을 useEffect로 이동하세요.

    관련내용을 검색하여 찾아보니
    컴포넌트가 마운트 되기 전에 상태를 불러왔기 때문에 발생한 에러이고
    보통의 마운트가 완료된 이후 데이터를 호출하기 위하여 useEffect(() => {} ,[]) 안에 호출 매서드를 넣어주었습니다.
    하지만 useRecoilValue 는 react Hook이기 때문에 useEffect의 안에서 사용할 수 없습니다.

Selector 비동기 처리

async function fetchDataFromFirebase() {
    try {
        const querySnapshot: QuerySnapshot = await getDocs(
            collection(db, 'show'),
        )
        const data = querySnapshot.docs.map((doc) => {
            const postData = doc.data()
            const post: Partial<PostState> = {
                id: doc.id,
                ...postData,
            }
            return post as PostState
        })
        return data
    } catch (error) {
        console.error('error', error)
        return []
    }
}

export const getAllPostSelectors = selector<PostState[]>({
    key: 'getAllPostsSelector',
    get: async function fetchData() {
        const data = await fetchDataFromFirebase()
        return data
    },
})

해결방안

공식 홈페이지에서 recoil은 보류 중인 데이터를 다루기 위해 React Suspense와 함께 동작하도록 디자인되어 있다고 말합니다.
<React.Suspense fallback={<div>Loading...</div>}>

React.Suspense

컴포넌트가 읽어 들이고 있는 데이터가 아직 준비되지 않았다고 React에 알려주는 일종의 메커니즘입니다.

데이터 불러오기 시작 → 렌더링 시작 → 데이터 불러오기 완료 순서로 동작하는 데이터 호출 로직에서 데이터 호출 완료 여부를 인지하여 데이터 불러오기를 완료할 때까지 fallback 속성 값으로 넣어준 컴포넌트를 표시합니다.

위의 방법과 또 다른 방법이 있을거같아서 찾아보니

useRecoilValueLoadable, useMemo

recoil 에서 제공하는 useRecoilValueLoadable 과 react 의 useMemo 를 사용해서 렌더링하는 방식이 있었습니다.

export function ShowList() {
    const boardList = useRecoilValueLoadable(getAllPostSelectors)

    const rows = useMemo(() => {
        return boardList?.state === 'hasValue' ? boardList?.contents : []
    }, [boardList])

    return (
        <S.Container>
            <Title text={'전체보기'} size="h2" />
            <S.PostWrap>
                {rows.map((post) => (
                    <PostItem key={post.id} post={post} />
                ))}
            </S.PostWrap>
        </S.Container>
    )
}

useRecoilValueLoadable 이해하기

useRecoilValueLoadable은 Recoil의 훅 중 하나로, Recoil 상태를 읽어오는 동시에 해당 상태의 로딩 상태를 알려줍니다. 이를 통해 비동기적으로 데이터를 가져올 때 로딩 상태를 효과적으로 다룰 수 있습니다.

state : 현재 Recoil 상태를 나타냅니다.

contents : atom 이나 selector 의 값이며, 상태에 따라 다른 값을 갖고 있습니다.

이외에도 다른속성

  • 'hasValue': Recoil 상태에 값이 성공적으로 로드되었음을 나타냅니다.
  • 'loading': Recoil 상태가 아직 로딩 중임을 나타냅니다.
  • 'hasError': Recoil 상태 로드 중 오류가 발생했음을 나타냅니다.

useMemo를 활용한 효율적인 렌더링

useMemo는 React의 훅 중 하나로, 메모이제이션된 값을 반환합니다. 이는 특히 계산 비용이 높은 작업이나 렌더링 성능을 최적화해야 하는 경우 유용합니다.

profile
프론트엔드를 공부하는 주니어 개발자입니다.
post-custom-banner

0개의 댓글