Skip to content

Commit

Permalink
docs: add docs for <QueryClientConsumer> (#1315)
Browse files Browse the repository at this point in the history
# Overview

I added docs for QueryClientConsumer

| ko | en |
| --- | --- |

|![image](https://github.com/user-attachments/assets/c8a12aa1-5845-4887-9f45-4ae445f3dee3)|![image](https://github.com/user-attachments/assets/7152703f-af23-4641-a42b-d828da5c6a85)|


## PR Checklist

- [x] I did below actions if need

1. I read the [Contributing
Guide](https://github.com/toss/suspensive/blob/main/CONTRIBUTING.md)
2. I added documents and tests.

---------

Co-authored-by: Jonghyeon Ko <[email protected]>
  • Loading branch information
gwansikk and manudeli authored Oct 19, 2024
1 parent 3b4080c commit c906edb
Show file tree
Hide file tree
Showing 4 changed files with 244 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { Callout } from '@/components'

# QueryClientConsumer

<Callout type='experimental'>

`<QueryClientConsumer/>` is an experimental feature, so this interface may change.

</Callout>

This component allows the use of `useQueryClient` within JSX to bypass the constraints of React hooks.

The props interface varies depending on the version of `@tanstack/react-query` installed in the user environment. If version 4 is installed, you need to use `props.context`. For version 5, you must use `props.queryClient`.
If the QueryClient is not passed through props, the component will use the nearest available QueryClient.

```tsx /QueryClientConsumer/
import { useSuspenseQuery } from '@tanstack/react-query'
import { QueryClientConsumer } from '@suspensive/react-query'

const PostsPage = () => {
const { data: posts } = useSuspenseQuery({
queryKey: ['posts'],
queryFn: () => getPosts(),
})

return (
<>
<QueryClientConsumer>
{(queryClient) => (
<button
onClick={() =>
queryClient.invalidateQueries({
queryKey: ['posts'],
})
}
>
Posts refresh
</button>
)}
</QueryClientConsumer>
{posts.map((post) => (
<Post key={post.id} {...post} />
))}
</>
)
}
```

### props.queryClient (@tanstack/react-query v5)

If you are using `@tanstack/react-query` version 5, you can access the desired QueryClient through `props.queryClient`.

```tsx /QueryClientConsumer/
import { QueryClient } from '@tanstack/react-query'
import { QueryClientConsumer } from '@suspensive/react-query'

const queryClient = new QueryClient()

const Example = () => {
return (
{/* 5️⃣ Use props.queryClient if you are working with @tanstack/react-query v5 */}
<QueryClientConsumer queryClient={queryClient}>
{(queryClient) => (<>{/* Use the QueryClient passed via props.queryClient */}</>)}
</QueryClientConsumer>
)
}
```

### props.context (@tanstack/react-query v4)

If you are using `@tanstack/react-query` version 4, use `props.context` to access the desired QueryClient.

```tsx /QueryClientConsumer/
import { createContext } from 'react'
import { QueryClient } from '@tanstack/react-query'
import { QueryClientConsumer } from '@suspensive/react-query'

const queryClient = new QueryClient()
const queryClientContext = createContext<QueryClient>(queryClient)

const Example = () => {
return (
{/* 4️⃣ Use props.context if you are working with @tanstack/react-query v4 */}
<QueryClientConsumer context={queryClientContext}>
{(queryClient) => (<>{/* Use the QueryClient passed via props.context */}</>)}
</QueryClientConsumer>
)
}
```

### Motivation: React hooks can only be used in functional components

React's hook (useQueryClient) can only be used within functional components. However, there are cases when you need features like invalidateQueries even within legacy class components. In such cases, `<QueryClientConsumer/>` helps you overcome these limitations.

```tsx /QueryClientConsumer/
import React from 'react'
import { QueryClientConsumer } from '@suspensive/react-query'

class PostsRefreshButton extends React.Component {
render() {
return (
{/* ✅ Access the queryClient to invalidate queries within a legacy class component */}
<QueryClientConsumer>
{(queryClient) => (
<button
onClick={() =>
queryClient.invalidateQueries({
queryKey: ['posts'],
})
}
>
Posts refresh
</button>
)}
</QueryClientConsumer>
)
}
}

export default PostsRefreshButton
```
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,6 @@ export default {
SuspenseInfiniteQuery: { title: '<SuspenseInfiniteQuery/>' },
PrefetchQuery: { title: '<PrefetchQuery/>' },
PrefetchInfiniteQuery: { title: '<PrefetchInfiniteQuery/>' },
QueryClientConsumer: { title: '<QueryClientConsumer/>' },
QueryErrorBoundary: { title: '<QueryErrorBoundary/>' },
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { Callout } from '@/components'

# QueryClientConsumer

<Callout type='experimental'>

`<QueryClientConsumer/>`는 실험 기능이므로 이 인터페이스는 변경될 수 있습니다.

</Callout>

React hook의 제약을 피해 JSX 상에서 `useQueryClient`를 사용할 수 있게 하는 컴포넌트입니다.

사용자 환경에 설치되어 있는 `@tanstack/react-query`의 버전에 따라 props 인터페이스가 다릅니다. `@tanstack/react-query` v4 버전이 설치되어 있다면 `props.context`를 사용하고, v5버전 이면 `props.queryClient`를 사용해야 합니다.
props를 통해 QueryClient가 전달하지 않는다면, 컴포넌트와 가장 가까운 QueryClient를 사용하게 됩니다.

```tsx /QueryClientConsumer/
import { useSuspenseQuery } from '@tanstack/react-query'
import { QueryClientConsumer } from '@suspensive/react-query'

const PostsPage = () => {
const { data: posts } = useSuspenseQuery({
queryKey: ['posts'],
queryFn: () => getPosts(),
})

return (
<>
<QueryClientConsumer>
{(queryClient) => (
<button
onClick={() =>
queryClient.invalidateQueries({
queryKey: ['posts'],
})
}
>
Posts refresh
</button>
)}
</QueryClientConsumer>
{posts.map((post) => (
<Post key={post.id} {...post} />
))}
</>
)
}
```

### props.queryClient (@tanstack/react-query v5)

`@tanstack/react-query` v5가 설치된 환경이라면 `props.queryClient` 사용하여 원하는 QueryClient에 접근할 수 있습니다.

```tsx /QueryClientConsumer/
import { QueryClient } from '@tanstack/react-query'
import { QueryClientConsumer } from '@suspensive/react-query'

const queryClient = new QueryClient()

const Example = () => {
return (
{/* 5️⃣ @tanstack/react-query v5을 사용하신다면 props.queryClient를 사용할 수 있습니다. */}
<QueryClientConsumer queryClient={queryClient}>
{(queryClient) => (<>{/* props.queryClient로 전달된 QueryClinet를 사용합니다. */}</>)}
</QueryClientConsumer>
)
}
```

### props.context (@tanstack/react-query v4)

`@tanstack/react-query` v4가 설치된 환경이라면 `props.context`를 사용하여 원하는 QueryClient에 접근할 수 있습니다.

```tsx /QueryClientConsumer/
import { createContext } from 'react'
import { QueryClient } from '@tanstack/react-query'
import { QueryClientConsumer } from '@suspensive/react-query'

const queryClient = new QueryClient()
const queryClientContext = createContext<QueryClient>(queryClient)

const Example = () => {
return (
{/* 4️⃣ @tanstack/react-query v4을 사용하신다면 props.context 사용할 수 있습니다.*/}
<QueryClientConsumer context={queryClientContext}>
{(queryClient) => (<>{/* props.queryClient로 전달된 QueryClinet를 사용합니다. */}</>)}
</QueryClientConsumer>
)
}
```

### 동기: 리액트 훅은 함수형 컴포넌트에서만 사용 가능함

React의 훅(useQueryClient)은 함수형 컴포넌트에서만 사용할 수 있습니다. 하지만 레거시 클래스 컴포넌트에서도 쿼리 무효화(invalidateQueries)와 같은 기능이 필요할 때가 있습니다. 이때 `<QueryClientConsumer/>`를 사용하면 이러한 제약을 해결할 수 있습니다.

```tsx /QueryClientConsumer/
import React from 'react'
import { QueryClientConsumer } from '@suspensive/react-query'

class PostsRefreshButton extends React.Component {
render() {
return (
{/* ✅ 레거시 클래스 컴포넌트에서 queryClient를 접근하여 쿼리 무효화(invalidateQueries)를 할 수 있습니다. */}
<QueryClientConsumer>
{(queryClient) => (
<button
onClick={() =>
queryClient.invalidateQueries({
queryKey: ['posts'],
})
}
>
Posts refresh
</button>
)}
</QueryClientConsumer>
)
}
}

export default PostsRefreshButton
```
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ export default {
SuspenseInfiniteQuery: { title: '<SuspenseInfiniteQuery/>' },
PrefetchQuery: { title: '<PrefetchQuery/>' },
PrefetchInfiniteQuery: { title: '<PrefetchInfiniteQuery/>' },
QueryClientConsumer: { title: '<QueryClientConsumer/>' },
QueryErrorBoundary: { title: '<QueryErrorBoundary/>' },
}

0 comments on commit c906edb

Please sign in to comment.