https://velog.io/@cnsrn1874/react-query-and-react-context
이 글은 2023년 7월 22일에 올라온 TkDodo의 React Query and React Context를 번역한 글입니다.
useQuery를 사용하면서 `data?.property`를 사용해보신 적이 있나요? 타입스크립트 에러를 피하려고 이리저리 몸을 비틀고 계신다면 이 글을 읽어보시는 것을 추천드립니다.
(미리보기)
> 이건 약간의 딜레마입니다. 정의될 것을 아니까 여기서는 타입스크립트를 침묵시키고 data!.userName을 해야 할까요? 아니면 안전을 위해 data?.userName을 사용할까요(여기서는 가능하지만, 다른 상황에서는 쉽지 않을 수 있음)? 그냥 타입 가드로 if (!data) return null를 추가할까요? 아니면 useCurrentUserQuery를 호출하는 25곳 전부에 적절한 로딩과 에러 처리를 추가할까요?
솔직히 말해서 이 모든 방법은 차선책이라고 생각합니다. (제가 지금까지 아는 한) "절대 일어날 수 없는" 검사들로 코드 베이스를 가득 채우고 싶지는 않습니다. 하지만 (언제나 그랬듯) 타입스크립트가 옳으므로 무시하고 싶지도 않습니다.
> 문제는 암시적 종속성이 있다는 사실에서 비롯됩니다. 암시적 종속성은 애플리케이션 구조에 대한 우리의 지식과 머릿속에만 존재할 뿐 코드 자체에서는 보이지 않는 종속성입니다.
... 물론 해결책은 의존성을 명시적으로 만드는 것입니다. 그리고 React Context를 사용하는 것보다 좋은 방법은 없습니다.
> React Context에 대한 오해가 많으니 바로잡아 보겠습니다. React Context는 상태 관리자가 아닙니다. useState나 useReducer와 함께 사용하면 상태 관리의 좋은 해결책으로 보일 수 있지만, 솔직히 저는 이 접근 방식을 절대 좋아하지 않습니다.
...제 트윗에서도 이미 React Context는 의존성 주입 도구라고 언급한 바 있습니다. React Context로 컴포넌트가 동작하는 데 필요한 "것"을 정의할 수 있으며, 모든 부모는 해당 정보를 제공할 책임이 있습니다.
...context를 사용하면 중간자를 건너뛸 수 있습니다. useCurrentUserQuery를 사용하는 예시에서 의존성을 명시적으로 만드는 데 도움이 될 수 있습니다. 데이터 사용 가능성의 확인 검사를 생략하고 싶은 모든 컴포넌트에서 직접 useCurrentUserQuery를 읽는 게 아니라, React Context에서 읽어옵니다. 그리고 그 context는 첫 번째로 검사를 하는 부모에 의해 채워집니다.
```tsx
const CurrentUserContext = React.CreateContext<User | null>(null)
export const useCurrentUserContext = () => {
return React.useContext(CurrentUserContext)
}
export const CurrentUserContextProvider = ({
children,
}: {
children: React.ReactNode
}) => {
const currentUserQuery = useCurrentUserQuery()
if (currentUserQuery.isLoading) {
return <SkeletonLoader />
}
if (currentUserQuery.isError) {
return <ErrorMessage error={currentUserQuery.error} />
}
return (
<CurrentUserContext.Provider value={currentUserQuery.data}>
{children}
</CurrentUserContext.Provider>
)
}
```
여기서 currentUserQuery를 가져와서 (로딩과 에러 상태를 미리 제거함으로써) 결과 데이터가 존재한다면 React Context에 넣습니다. 그렇게 하면 UserNameDisplay 같은 자식 컴포넌트에서 그 context를 안전하게 읽을 수 있습니다.