···11+---
22+'@urql/next': major
33+---
44+55+Create `@urql/next` which is a package meant to support Next 13 and
66+the React 18 features contained within.
77+88+For server components we have `@urql/next/rsc` and for client components
99+just `@urql/next`.
+153-7
docs/advanced/server-side-rendering.md
···177177## Next.js
178178179179If you're using [Next.js](https://nextjs.org/) you can save yourself a lot of work by using
180180-`next-urql`. The `next-urql` package includes setup for `react-ssr-prepass` already, which automates
181181-a lot of the complexity of setting up server-side rendering with `urql`.
182182-183183-We have a custom integration with [`Next.js`](https://nextjs.org/), being [`next-urql`](https://github.com/urql-graphql/urql/tree/main/packages/next-urql)
184184-this integration contains convenience methods specifically for `Next.js`.
185185-These will simplify the above setup for SSR.
180180+`@urql/next`. The `@urql/next` package is set to work with Next 13.
186181187187-To set up `next-urql`, first we'll install `next-urql` with `react-is` and `urql` as
182182+To set up `@urql/next`, first we'll install `@urql/next` and `urql` as
188183peer dependencies:
184184+185185+```sh
186186+yarn add @urql/next urql graphql
187187+# or
188188+npm install --save @urql/next urql graphql
189189+```
190190+191191+We now have two ways to leverage `@urql/next`, one being part of a Server component
192192+or being part of the general `app/` folder.
193193+194194+In a server component we will import from `@urql/next/rsc`
195195+196196+```ts
197197+// app/page.tsx
198198+import React from 'react';
199199+import Head from 'next/head';
200200+import { cacheExchange, createClient, fetchExchange, gql } from '@urql/core';
201201+import { registerUrql } from '@urql/next/rsc';
202202+203203+const makeClient = () => {
204204+ return createClient({
205205+ url: 'https://trygql.formidable.dev/graphql/basic-pokedex',
206206+ exchanges: [cacheExchange, fetchExchange],
207207+ });
208208+};
209209+210210+const { getClient } = registerUrql(makeClient);
211211+212212+export default async function Home() {
213213+ const result = await getClient().query(PokemonsQuery, {});
214214+ return (
215215+ <main>
216216+ <h1>This is rendered as part of an RSC</h1>
217217+ <ul>
218218+ {result.data.pokemons.map((x: any) => (
219219+ <li key={x.id}>{x.name}</li>
220220+ ))}
221221+ </ul>
222222+ </main>
223223+ );
224224+}
225225+```
226226+227227+When we aren't leveraging server components we will import the things we will
228228+need to do a bit more setup, we go to the `client` component's layout file and
229229+structure it as the following.
230230+231231+```tsx
232232+// app/client/layout.tsx
233233+'use client';
234234+235235+import { UrqlProvider, ssrExchange, cacheExchange, fetchExchange, createClient } from '@urql/next';
236236+237237+const ssr = ssrExchange();
238238+const client = createClient({
239239+ url: 'https://trygql.formidable.dev/graphql/web-collections',
240240+ exchanges: [cacheExchange, ssr, fetchExchange],
241241+});
242242+243243+export default function Layout({ children }: React.PropsWithChildren) {
244244+ return (
245245+ <UrqlProvider client={client} ssr={ssr}>
246246+ {children}
247247+ </UrqlProvider>
248248+ );
249249+}
250250+```
251251+252252+It is important that we pas both a client as well as the `ssrExchange` to the `Provider`
253253+this way we will be able to restore the data that Next streams to the client later on
254254+when we are hydrating.
255255+256256+The next step is to query data in your client components by means of the `useQuery`
257257+method defined in `@urql/next`.
258258+259259+```tsx
260260+// app/client/page.tsx
261261+'use client';
262262+263263+import Link from 'next/link';
264264+import { Suspense } from 'react';
265265+import { useQuery, gql } from '@urql/next';
266266+267267+export default function Page() {
268268+ return (
269269+ <Suspense>
270270+ <Pokemons />
271271+ </Suspense>
272272+ );
273273+}
274274+275275+const PokemonsQuery = gql`
276276+ query {
277277+ pokemons(limit: 10) {
278278+ id
279279+ name
280280+ }
281281+ }
282282+`;
283283+284284+function Pokemons() {
285285+ const [result] = useQuery({ query: PokemonsQuery });
286286+ return (
287287+ <main>
288288+ <h1>This is rendered as part of SSR</h1>
289289+ <ul>
290290+ {result.data.pokemons.map((x: any) => (
291291+ <li key={x.id}>{x.name}</li>
292292+ ))}
293293+ </ul>
294294+ </main>
295295+ );
296296+}
297297+```
298298+299299+The data queried in the above component will be rendered on the server
300300+and re-hydrated back on the client. When using multiple Suspense boundaries
301301+these will also get flushed as they complete and re-hydrated.
302302+303303+> When data is used throughout the application we advise against
304304+> rendering this as part of a server-component so you can benefit
305305+> from the client-side cache.
306306+307307+### Invalidating data from a server-component
308308+309309+When data is rendered by a server component but you dispatch a mutation
310310+from a client component the server won't automatically know that the
311311+server-component on the client needs refreshing. You can forcefully
312312+tell the server to do so by using the Next router and calling `.refresh()`.
313313+314314+```tsx
315315+import { useRouter } from 'next/router';
316316+317317+const Todo = () => {
318318+ const router = useRouter();
319319+ const executeMutation = async () => {
320320+ await updateTodo();
321321+ router.refresh();
322322+ };
323323+};
324324+```
325325+326326+### Disabling RSC fetch caching
327327+328328+You can pass `fetchOptions: { cache: "no-store" }` to the `createClient`
329329+constructor to avoid running into cached fetches with server-components.
330330+331331+## Legacy Next.js (pages)
332332+333333+If you're using [Next.js](https://nextjs.org/) with the classic `pages` you can instead use `next-urql`.
334334+To set up `next-urql`, first we'll install `next-urql` with `react-is` and `urql` as peer dependencies:
189335190336```sh
191337yarn add next-urql react-is urql graphql
-47
examples/with-next/README.md
···2828npm install
2929npm run start
3030```
3131-3232-## getInitialProps
3333-3434-This is the output you'll get when you're using `{ ssr: true }`, this way urql will try to automate
3535-as much for you as it possibly can by using a [`prepass`](https://github.com/FormidableLabs/react-ssr-prepass)
3636-this means that every `useQuery` used in your virtual-dom will be ran, the data will be collected on the server
3737-and hydrated on the client.
3838-3939-> NOTE: to reduce performance complexities try to keep this to top-level renders as this can amount to waterfalls.
4040-4141-## getStaticProps
4242-4343-This requires some manual work, when we look at [`static.js`](./pages/static.js) we can see that we define our own
4444-`getStaticProps` method, this because these methods are only `user-facing`. When doing a `yarn next build` we'll need to
4545-ensure that the server we're targetting is running so we can successfully execute the static prerender.
4646-4747-## getServerSideProps
4848-4949-This requires some manual work, when we look at [`server.js`](./pages/server.js) we can see that we define our own
5050-`getServerSideProps` method, this because these methods are only `user-facing`.
5151-5252-## Output
5353-5454-We can see that our `/` and `/server` routes are rendered on the server and `/static` is statically prerendered.
5555-5656-```
5757-Page Size First Load JS
5858-┌ λ / 4.98 kB 90 kB
5959-├ /_app 0 B 85 kB
6060-├ ○ /404 3.46 kB 88.5 kB
6161-├ λ /api/graphql 0 B 85 kB
6262-├ λ /server 878 B 85.9 kB
6363-└ ● /static 895 B 85.9 kB
6464-+ First Load JS shared by all 85 kB
6565- ├ chunks/d8c192fcf6e34535672c13f111ef41e3832b265d.d03071.js 17.4 kB
6666- ├ chunks/f6078781a05fe1bcb0902d23dbbb2662c8d200b3.6a2b27.js 13.3 kB
6767- ├ chunks/framework.4b1bec.js 41.8 kB
6868- ├ chunks/main.3d1d43.js 7.14 kB
6969- ├ chunks/pages/_app.92bde8.js 4.68 kB
7070- └ chunks/webpack.50bee0.js 751 B
7171-7272-7373-λ (Server) server-side renders at runtime (uses getInitialProps or getServerSideProps)
7474-○ (Static) automatically rendered as static HTML (uses no initial props)
7575-● (SSG) automatically generated as static HTML + JSON (uses getStaticProps)
7676- (ISR) incremental static regeneration (uses revalidate in getStaticProps)
7777-```
···11+import Link from 'next/link';
22+import { cacheExchange, createClient, fetchExchange, gql } from '@urql/core';
33+import { registerUrql } from '@urql/next/rsc';
44+55+const makeClient = () => {
66+ return createClient({
77+ url: 'https://graphql-pokeapi.graphcdn.app/',
88+ exchanges: [cacheExchange, fetchExchange],
99+ });
1010+};
1111+1212+const { getClient } = registerUrql(makeClient);
1313+1414+const PokemonsQuery = gql`
1515+ query {
1616+ pokemons(limit: 10) {
1717+ results {
1818+ id
1919+ name
2020+ }
2121+ }
2222+ }
2323+`;
2424+2525+export default async function Home() {
2626+ const result = await getClient().query(PokemonsQuery, {});
2727+ return (
2828+ <main>
2929+ <h1>This is rendered as part of an RSC</h1>
3030+ <ul>
3131+ {result.data
3232+ ? result.data.pokemons.results.map((x: any) => (
3333+ <li key={x.id}>{x.name}</li>
3434+ ))
3535+ : JSON.stringify(result.error)}
3636+ </ul>
3737+ <Link href="/non-rsc">Non RSC</Link>
3838+ </main>
3939+ );
4040+}
+5
examples/with-next/next-env.d.ts
···11+/// <reference types="next" />
22+/// <reference types="next/image-types/global" />
33+44+// NOTE: This file should not be edited
55+// see https://nextjs.org/docs/basic-features/typescript for more information.
···11-export { withUrqlClient } from './with-urql-client';
22-export { initUrqlClient } from './init-urql-client';
33-export * from './types';
11+export * from 'urql';
22+export { useQuery } from './useQuery';
33+export { UrqlProvider } from './Provider';
-53
packages/next-urql/src/init-urql-client.ts
···11-import { Client, ClientOptions, createClient } from '@urql/core';
22-33-let urqlClient: Client | null = null;
44-55-/** Resets the `Client` that {@link initUrqlClient} returns.
66- *
77- * @remarks
88- * `resetClient` will force {@link initUrqlClient} to create a new
99- * {@link Client}, rather than reusing the same `Client` it already
1010- * created on the client-side.
1111- *
1212- * This may be used to force the cache and any state in the `Client`
1313- * to be cleared and reset.
1414- */
1515-export function resetClient() {
1616- urqlClient = null;
1717-}
1818-1919-/** Creates a {@link Client} the given options.
2020- *
2121- * @param clientOptions - {@link ClientOptions} to create the `Client` with.
2222- * @param canEnableSuspense - Enables React Suspense on the server-side for `react-ssr-prepass`.
2323- * @returns the created {@link Client}
2424- *
2525- * @remarks
2626- * `initUrqlClient` creates a {@link Client} with the given options,
2727- * like {@link createClient} does, but reuses the same client when
2828- * run on the client-side.
2929- *
3030- * As long as `canEnableSuspense` is set to `true`, it enables React Suspense
3131- * mode on the server-side for `react-ssr-prepass`.
3232- */
3333-export function initUrqlClient(
3434- clientOptions: ClientOptions,
3535- canEnableSuspense: boolean
3636-): Client {
3737- // Create a new Client for every server-side rendered request.
3838- // This ensures we reset the state for each rendered page.
3939- // If there is an exising client instance on the client-side, use it.
4040- const isServer = typeof window === 'undefined';
4141- if (isServer || !urqlClient) {
4242- urqlClient = createClient({
4343- ...clientOptions,
4444- suspense: canEnableSuspense && (isServer || clientOptions.suspense),
4545- });
4646- // Serialize the urqlClient to null on the client-side.
4747- // This ensures we don't share client and server instances of the urqlClient.
4848- (urqlClient as any).toJSON = () => null;
4949- }
5050-5151- // Return both the Client instance and the ssrCache.
5252- return urqlClient;
5353-}
+31
packages/next-urql/src/rsc.ts
···11+import * as React from 'react';
22+import { Client } from '@urql/core';
33+44+/** Function to cache an urql-client across React Server Components.
55+ *
66+ * @param makeClient - A function that creates an urql-client.
77+ * @returns an object containing a getClient method.
88+ *
99+ * @example
1010+ * ```ts
1111+ * import { cacheExchange, createClient, fetchExchange, gql } from '@urql/core';
1212+ * import { registerUrql } from '@urql/next/rsc';
1313+ * const makeClient = () => {
1414+ * return createClient({
1515+ * url: 'https://trygql.formidable.dev/graphql/basic-pokedex',
1616+ * exchanges: [cacheExchange, fetchExchange],
1717+ * });
1818+ * };
1919+ *
2020+ * const { getClient } = registerUrql(makeClient);
2121+ * ```
2222+ */
2323+export function registerUrql(makeClient: () => Client): {
2424+ getClient: () => Client;
2525+} {
2626+ // @ts-ignore you exist don't worry
2727+ const getClient = React.cache(makeClient);
2828+ return {
2929+ getClient,
3030+ };
3131+}
-102
packages/next-urql/src/types.ts
···11-import type { ClientOptions, Client, SSRExchange, SSRData } from '@urql/core';
22-import type { NextPageContext } from 'next';
33-import type { AppContext } from 'next/app';
44-55-/** The Next.js {@link NextPageContext}, as modified by `next-urql`. */
66-export interface NextUrqlPageContext extends NextPageContext {
77- urqlClient: Client;
88-}
99-1010-/** The Next.js {@link AppContext}, as modified by `next-urql`. */
1111-export interface NextUrqlAppContext extends AppContext {
1212- urqlClient: Client;
1313-}
1414-1515-export type NextUrqlContext = NextUrqlPageContext | NextUrqlAppContext;
1616-1717-/** Passed to {@link withUrqlClient} returning the options a {@link Client} is created with.
1818- *
1919- * @param ssrExchange - the `ssrExchange` you must use in your `exchanges` array.
2020- * @param ctx - Passed when `getInitialProps` is used and set to Next.js’ {@link NextPageContext}.
2121- * @returns a {@link ClientOptions} configuration object to create a {@link Client} with.
2222- *
2323- * @remarks
2424- * You must define a `getClientConfig` function and pass it to {@link withUrqlClient}.
2525- *
2626- * This function defines the options passed to {@link initUrqlClient}.
2727- * It passes you an `ssrExchange` that you must use in your `exchanges` array.
2828- *
2929- * @example
3030- * ```ts
3131- * import { cacheExchange, fetchExchange } from '@urql/core';
3232- * import { withUrqlClient } from 'next-urql';
3333- *
3434- * const WrappedPage = withUrqlClient(
3535- * (ssrExchange) => ({
3636- * url: 'https://YOUR_API',
3737- * exchanges: [cacheExchange, ssrExchange, fetchExchange],
3838- * })
3939- * )(Page);
4040- * ```
4141- */
4242-export type NextUrqlClientConfig = (
4343- ssrExchange: SSRExchange,
4444- ctx?: NextPageContext
4545-) => ClientOptions;
4646-4747-/** Props that {@link withUrqlClient} components pass on to your component. */
4848-export interface WithUrqlProps {
4949- /** The {@link Client} that {@link withUrqlClient} created for your component. */
5050- urqlClient?: Client;
5151- /** Next.js’ `pageProps` prop, as passed to it by Next.js. */
5252- pageProps: any;
5353- /** The SSR data that {@link withUrqlClient} created for your component. */
5454- urqlState?: SSRData;
5555- /** Resets the `Client` that on the client-side.
5656- *
5757- * @remarks
5858- * `resetUrqlClient` will force a new {@link Client} to be created
5959- * on the client-side, rather than the same `Client` with the same
6060- * server-side data to be reused.
6161- *
6262- * This may be used to force the cache and any state in the `Client`
6363- * to be cleared and reset.
6464- */
6565- resetUrqlClient?(): void;
6666- [key: string]: any;
6767-}
6868-6969-/** Options that may be passed to the {@link withUrqlClient} wrapper function. */
7070-export interface WithUrqlClientOptions {
7171- /** Enables automatic server-side rendering mode.
7272- *
7373- * @remarks
7474- * When enabled, {@link withUrqlClient} will add a `getInitialProps`
7575- * function to the resulting component, even if you haven't defined
7676- * one.
7777- *
7878- * This function will automatically capture `urql`'s SSR state on the
7979- * server-side and rehydrate it on the client-side, unless
8080- * {@link WithUrqlClientOptions.neverSuspend} is `true`.
8181- */
8282- ssr?: boolean;
8383- /** Disables automatic server-side rendering, even if a `getInitialProps` function is defined.
8484- *
8585- * @remarks
8686- * When enabled, {@link withUrqlClient} will never execute queries
8787- * on the server-side automatically, and will instead rely on you
8888- * to do so manually.
8989- */
9090- neverSuspend?: boolean;
9191- /** Enables reexecuting operations on the client-side after rehydration.
9292- *
9393- * @remarks
9494- * When enabled, `staleWhileRevalidate` will reexecute GraphQL queries on
9595- * the client-side, if they’ve been rehydrated from SSR state.
9696- *
9797- * This is useful if you, for instance, cache your server-side rendered
9898- * pages, or if you use `getStaticProps` and wish to get this data
9999- * updated.
100100- */
101101- staleWhileRevalidate?: boolean;
102102-}
+214
packages/next-urql/src/useQuery.ts
···11+'use client';
22+33+import {
44+ AnyVariables,
55+ CombinedError,
66+ GraphQLRequestParams,
77+ Operation,
88+ OperationContext,
99+ RequestPolicy,
1010+ createRequest,
1111+ useQuery as orig_useQuery,
1212+} from 'urql';
1313+import { useUrqlValue } from './useUrqlValue';
1414+1515+/** Input arguments for the {@link useQuery} hook.
1616+ *
1717+ * @param query - The GraphQL query that `useQuery` executes.
1818+ * @param variables - The variables for the GraphQL query that `useQuery` executes.
1919+ */
2020+export type UseQueryArgs<
2121+ Variables extends AnyVariables = AnyVariables,
2222+ Data = any
2323+> = {
2424+ /** Updates the {@link RequestPolicy} for the executed GraphQL query operation.
2525+ *
2626+ * @remarks
2727+ * `requestPolicy` modifies the {@link RequestPolicy} of the GraphQL query operation
2828+ * that `useQuery` executes, and indicates a caching strategy for cache exchanges.
2929+ *
3030+ * For example, when set to `'cache-and-network'`, {@link useQuery} will
3131+ * receive a cached result with `stale: true` and an API request will be
3232+ * sent in the background.
3333+ *
3434+ * @see {@link OperationContext.requestPolicy} for where this value is set.
3535+ */
3636+ requestPolicy?: RequestPolicy;
3737+ /** Updates the {@link OperationContext} for the executed GraphQL query operation.
3838+ *
3939+ * @remarks
4040+ * `context` may be passed to {@link useQuery}, to update the {@link OperationContext}
4141+ * of a query operation. This may be used to update the `context` that exchanges
4242+ * will receive for a single hook.
4343+ *
4444+ * Hint: This should be wrapped in a `useMemo` hook, to make sure that your
4545+ * component doesn’t infinitely update.
4646+ *
4747+ * @example
4848+ * ```ts
4949+ * const [result, reexecute] = useQuery({
5050+ * query,
5151+ * context: useMemo(() => ({
5252+ * additionalTypenames: ['Item'],
5353+ * }), [])
5454+ * });
5555+ * ```
5656+ */
5757+ context?: Partial<OperationContext>;
5858+ /** Prevents {@link useQuery} from automatically executing GraphQL query operations.
5959+ *
6060+ * @remarks
6161+ * `pause` may be set to `true` to stop {@link useQuery} from executing
6262+ * automatically. The hook will stop receiving updates from the {@link Client}
6363+ * and won’t execute the query operation, until either it’s set to `false`
6464+ * or the {@link UseQueryExecute} function is called.
6565+ *
6666+ * @see {@link https://urql.dev/goto/docs/basics/react-preact/#pausing-usequery} for
6767+ * documentation on the `pause` option.
6868+ */
6969+ pause?: boolean;
7070+} & GraphQLRequestParams<Data, Variables>;
7171+7272+/** State of the current query, your {@link useQuery} hook is executing.
7373+ *
7474+ * @remarks
7575+ * `UseQueryState` is returned (in a tuple) by {@link useQuery} and
7676+ * gives you the updating {@link OperationResult} of GraphQL queries.
7777+ *
7878+ * Even when the query and variables passed to {@link useQuery} change,
7979+ * this state preserves the prior state and sets the `fetching` flag to
8080+ * `true`.
8181+ * This allows you to display the previous state, while implementing
8282+ * a separate loading indicator separately.
8383+ */
8484+export interface UseQueryState<
8585+ Data = any,
8686+ Variables extends AnyVariables = AnyVariables
8787+> {
8888+ /** Indicates whether `useQuery` is waiting for a new result.
8989+ *
9090+ * @remarks
9191+ * When `useQuery` is passed a new query and/or variables, it will
9292+ * start executing the new query operation and `fetching` is set to
9393+ * `true` until a result arrives.
9494+ *
9595+ * Hint: This is subtly different than whether the query is actually
9696+ * fetching, and doesn’t indicate whether a query is being re-executed
9797+ * in the background. For this, see {@link UseQueryState.stale}.
9898+ */
9999+ fetching: boolean;
100100+ /** Indicates that the state is not fresh and a new result will follow.
101101+ *
102102+ * @remarks
103103+ * The `stale` flag is set to `true` when a new result for the query
104104+ * is expected and `useQuery` is waiting for it. This may indicate that
105105+ * a new request is being requested in the background.
106106+ *
107107+ * @see {@link OperationResult.stale} for the source of this value.
108108+ */
109109+ stale: boolean;
110110+ /** The {@link OperationResult.data} for the executed query. */
111111+ data?: Data;
112112+ /** The {@link OperationResult.error} for the executed query. */
113113+ error?: CombinedError;
114114+ /** The {@link OperationResult.extensions} for the executed query. */
115115+ extensions?: Record<string, any>;
116116+ /** The {@link Operation} that the current state is for.
117117+ *
118118+ * @remarks
119119+ * This is the {@link Operation} that is currently being executed.
120120+ * When {@link UseQueryState.fetching} is `true`, this is the
121121+ * last `Operation` that the current state was for.
122122+ */
123123+ operation?: Operation<Data, Variables>;
124124+}
125125+126126+/** Triggers {@link useQuery} to execute a new GraphQL query operation.
127127+ *
128128+ * @param opts - optionally, context options that will be merged with the hook's
129129+ * {@link UseQueryArgs.context} options and the `Client`’s options.
130130+ *
131131+ * @remarks
132132+ * When called, {@link useQuery} will re-execute the GraphQL query operation
133133+ * it currently holds, even if {@link UseQueryArgs.pause} is set to `true`.
134134+ *
135135+ * This is useful for executing a paused query or re-executing a query
136136+ * and get a new network result, by passing a new request policy.
137137+ *
138138+ * ```ts
139139+ * const [result, reexecuteQuery] = useQuery({ query });
140140+ *
141141+ * const refresh = () => {
142142+ * // Re-execute the query with a network-only policy, skipping the cache
143143+ * reexecuteQuery({ requestPolicy: 'network-only' });
144144+ * };
145145+ * ```
146146+ */
147147+export type UseQueryExecute = (opts?: Partial<OperationContext>) => void;
148148+149149+/** Result tuple returned by the {@link useQuery} hook.
150150+ *
151151+ * @remarks
152152+ * Similarly to a `useState` hook’s return value,
153153+ * the first element is the {@link useQuery}’s result and state,
154154+ * a {@link UseQueryState} object,
155155+ * and the second is used to imperatively re-execute the query
156156+ * via a {@link UseQueryExecute} function.
157157+ */
158158+export type UseQueryResponse<
159159+ Data = any,
160160+ Variables extends AnyVariables = AnyVariables
161161+> = [UseQueryState<Data, Variables>, UseQueryExecute];
162162+163163+/** Hook to run a GraphQL query and get updated GraphQL results.
164164+ *
165165+ * @param args - a {@link UseQueryArgs} object, to pass a `query`, `variables`, and options.
166166+ * @returns a {@link UseQueryResponse} tuple of a {@link UseQueryState} result, and re-execute function.
167167+ *
168168+ * @remarks
169169+ * `useQuery` allows GraphQL queries to be defined and executed.
170170+ * Given {@link UseQueryArgs.query}, it executes the GraphQL query with the
171171+ * context’s {@link Client}.
172172+ *
173173+ * The returned result updates when the `Client` has new results
174174+ * for the query, and changes when your input `args` change.
175175+ *
176176+ * Additionally, if the `suspense` option is enabled on the `Client`,
177177+ * the `useQuery` hook will suspend instead of indicating that it’s
178178+ * waiting for a result via {@link UseQueryState.fetching}.
179179+ *
180180+ * @see {@link https://urql.dev/goto/urql/docs/basics/react-preact/#queries} for `useQuery` docs.
181181+ *
182182+ * @example
183183+ * ```ts
184184+ * import { gql, useQuery } from 'urql';
185185+ *
186186+ * const TodosQuery = gql`
187187+ * query { todos { id, title } }
188188+ * `;
189189+ *
190190+ * const Todos = () => {
191191+ * const [result, reexecuteQuery] = useQuery({
192192+ * query: TodosQuery,
193193+ * variables: {},
194194+ * });
195195+ * // ...
196196+ * };
197197+ * ```
198198+ */
199199+export function useQuery<
200200+ Data = any,
201201+ Variables extends AnyVariables = AnyVariables
202202+>(args: UseQueryArgs<Variables, Data>): UseQueryResponse<Data, Variables> {
203203+ const request = createRequest(
204204+ args.query,
205205+ (args.variables || {}) as AnyVariables
206206+ );
207207+ useUrqlValue(request.key);
208208+209209+ const [result, execute] = orig_useQuery(args);
210210+211211+ useUrqlValue(request.key);
212212+213213+ return [result, execute];
214214+}
+60
packages/next-urql/src/useUrqlValue.ts
···11+'use client';
22+33+import React from 'react';
44+import { useDataHydrationContext } from './DataHydrationContext';
55+import { SSRContext } from './Provider';
66+77+export const symbolString = 'urql_transport';
88+export const urqlTransportSymbol = Symbol.for(symbolString);
99+1010+export type UrqlResult = { data?: any; error?: any; extensions?: any };
1111+1212+export function useUrqlValue(operationKey: number): void {
1313+ const ssrExchange = React.useContext(SSRContext);
1414+ const rehydrationContext = useDataHydrationContext();
1515+1616+ if (!ssrExchange) {
1717+ throw new Error(
1818+ 'Missing "UrqlProvider" component as a parent or did not pass in an "ssrExchange" to the Provider.'
1919+ );
2020+ }
2121+2222+ if (typeof window == 'undefined') {
2323+ const data = ssrExchange.extractData();
2424+ if (rehydrationContext && data[operationKey]) {
2525+ const res = data[operationKey];
2626+ const parsed = {
2727+ ...res,
2828+ extensions: res.extensions
2929+ ? JSON.parse(res.extensions)
3030+ : res.extensions,
3131+ data: res.data ? JSON.parse(res.data) : res.data,
3232+ error: res.error,
3333+ };
3434+ rehydrationContext.operationValuesByKey[operationKey] = parsed;
3535+ }
3636+ } else {
3737+ const stores = (window[urqlTransportSymbol as any] ||
3838+ []) as unknown as Array<{
3939+ rehydrate: Record<number, UrqlResult>;
4040+ }>;
4141+4242+ const store = stores.find(
4343+ x => x && x.rehydrate && x.rehydrate[operationKey]
4444+ );
4545+ if (store) {
4646+ const result = store.rehydrate && store.rehydrate[operationKey];
4747+ if (result) {
4848+ delete store.rehydrate[operationKey];
4949+ ssrExchange.restoreData({
5050+ [operationKey]: {
5151+ extensions: JSON.stringify(result.extensions),
5252+ data: JSON.stringify(result.data),
5353+ error: result.error,
5454+ },
5555+ });
5656+ delete store.rehydrate[operationKey];
5757+ }
5858+ }
5959+ }
6060+}
-199
packages/next-urql/src/with-urql-client.ts
···11-import type { ReactNode, ReactElement } from 'react';
22-import * as React from 'react';
33-44-import {
55- Provider,
66- SSRExchange,
77- ssrExchange,
88- cacheExchange,
99- fetchExchange,
1010-} from 'urql';
1111-1212-import ssrPrepass from 'react-ssr-prepass';
1313-import { NextComponentType, NextPage, NextPageContext } from 'next';
1414-import NextApp, { AppContext } from 'next/app';
1515-1616-import { initUrqlClient, resetClient } from './init-urql-client';
1717-1818-import {
1919- NextUrqlClientConfig,
2020- NextUrqlContext,
2121- WithUrqlProps,
2222- WithUrqlClientOptions,
2323-} from './types';
2424-2525-let ssr: SSRExchange;
2626-type NextPageWithLayout = NextPage & {
2727- getLayout?: (page: ReactElement) => ReactNode;
2828-};
2929-3030-/** Creates a wrapper for Next.js Page, App, or Document components that rehydrates `urql` state.
3131- *
3232- * @param getClientConfig - function used to create the {@link Client}
3333- * @param options - optional {@link WithUrqlClientOptions} configuration options
3434- * @returns a higher-order component function, which you can pass a Next.js page or app component.
3535- *
3636- * @remarks
3737- * Used to wrap a Next.js page or app component. It will create a {@link Client} and passes
3838- * it on to the child component and adds a React Provider for it.
3939- *
4040- * It will restore any page’s `pageProps.urqlState` with the {@link SSRExchange} and also
4141- * supports doing this automatically when the {@link WithUrqlClientOptions.ssr} option
4242- * is enabled.
4343- *
4444- * If you don’t define the above option, you will have to write `getServerSideProps` or
4545- * `getStaticProps` methods on your component manually.
4646- *
4747- * @see {@link https://urql.dev/goto/docs/advanced/server-side-rendering/#nextjs} for more documentation.
4848- *
4949- * @example
5050- * ```ts
5151- * import { cacheExchange, fetchExchange } from '@urql/core';
5252- * import { withUrqlClient } from 'next-urql';
5353- *
5454- * const WrappedPage = withUrqlClient(
5555- * (ssrExchange) => ({
5656- * url: 'https://YOUR_API',
5757- * exchanges: [cacheExchange, ssrExchange, fetchExchange],
5858- * }),
5959- * { ssr: true },
6060- * )(Page);
6161- * ```
6262- */
6363-export function withUrqlClient(
6464- getClientConfig: NextUrqlClientConfig,
6565- options?: WithUrqlClientOptions
6666-) {
6767- if (!options) options = {};
6868-6969- return <C extends NextPage<any> | typeof NextApp>(
7070- AppOrPage: C
7171- ): NextComponentType<NextUrqlContext, {}, WithUrqlProps> => {
7272- const shouldEnableSuspense = Boolean(
7373- (AppOrPage.getInitialProps || options!.ssr) && !options!.neverSuspend
7474- );
7575-7676- const WithUrql = ({
7777- pageProps,
7878- urqlClient,
7979- urqlState,
8080- ...rest
8181- }: WithUrqlProps) => {
8282- const [version, forceUpdate] = React.useReducer(prev => prev + 1, 0);
8383- const urqlServerState = (pageProps && pageProps.urqlState) || urqlState;
8484-8585- const client = React.useMemo(() => {
8686- if (urqlClient && !version) {
8787- return urqlClient;
8888- }
8989-9090- if (!ssr || typeof window === 'undefined') {
9191- // We want to force the cache to hydrate, we do this by setting the isClient flag to true
9292- ssr = ssrExchange({
9393- initialState: urqlServerState,
9494- isClient: true,
9595- staleWhileRevalidate:
9696- typeof window !== 'undefined'
9797- ? options!.staleWhileRevalidate
9898- : undefined,
9999- });
100100- } else if (!version) {
101101- ssr.restoreData(urqlServerState);
102102- }
103103-104104- const clientConfig = getClientConfig(ssr);
105105- if (!clientConfig.exchanges) {
106106- // When the user does not provide exchanges we make the default assumption.
107107- clientConfig.exchanges = [cacheExchange, ssr, fetchExchange];
108108- }
109109-110110- return initUrqlClient(clientConfig, shouldEnableSuspense)!;
111111- }, [urqlClient, urqlServerState, version]);
112112-113113- const resetUrqlClient = React.useCallback(() => {
114114- resetClient();
115115- ssr = ssrExchange({ initialState: undefined });
116116- forceUpdate();
117117- }, []);
118118-119119- return React.createElement(
120120- Provider,
121121- { value: client },
122122- React.createElement(AppOrPage, {
123123- ...rest,
124124- pageProps,
125125- urqlClient: client,
126126- resetUrqlClient,
127127- })
128128- );
129129- };
130130-131131- // Set the displayName to indicate use of withUrqlClient.
132132- const displayName =
133133- (AppOrPage as any).displayName || AppOrPage.name || 'Component';
134134- WithUrql.displayName = `withUrqlClient(${displayName})`;
135135-136136- if ((AppOrPage as NextPageWithLayout).getLayout) {
137137- WithUrql.getLayout = (AppOrPage as NextPageWithLayout).getLayout;
138138- }
139139-140140- if (AppOrPage.getInitialProps || options!.ssr) {
141141- WithUrql.getInitialProps = async (appOrPageCtx: NextUrqlContext) => {
142142- const AppTree = appOrPageCtx.AppTree!;
143143-144144- // Determine if we are wrapping an App component or a Page component.
145145- const isApp = !!(appOrPageCtx as AppContext).Component;
146146- const ctx = isApp
147147- ? (appOrPageCtx as AppContext).ctx
148148- : (appOrPageCtx as NextPageContext);
149149-150150- const ssrCache = ssrExchange({ initialState: undefined });
151151- const clientConfig = getClientConfig(ssrCache, ctx);
152152- if (!clientConfig.exchanges) {
153153- // When the user does not provide exchanges we make the default assumption.
154154- clientConfig.exchanges = [cacheExchange, ssrCache, fetchExchange];
155155- }
156156-157157- const urqlClient = initUrqlClient(clientConfig, !options!.neverSuspend);
158158-159159- if (urqlClient) {
160160- (ctx as NextUrqlContext).urqlClient = urqlClient;
161161- }
162162-163163- // Run the wrapped component's getInitialProps function.
164164- let pageProps = {} as any;
165165- if (AppOrPage.getInitialProps) {
166166- pageProps = await AppOrPage.getInitialProps(appOrPageCtx as any);
167167- if (ctx.res && (ctx.res.writableEnded || ctx.res.finished)) {
168168- return { ...pageProps, urqlClient };
169169- }
170170- }
171171-172172- // Check the window object to determine whether or not we are on the server.
173173- // getInitialProps runs on the server for initial render, and on the client for navigation.
174174- // We only want to run the prepass step on the server.
175175- if (typeof window !== 'undefined') {
176176- return { ...pageProps, urqlClient };
177177- }
178178-179179- const props = { ...pageProps, urqlClient };
180180- const appTreeProps = isApp
181181- ? { pageProps: {}, ...props }
182182- : { pageProps: props };
183183-184184- // Run the prepass step on AppTree. This will run all urql queries on the server.
185185- if (!options!.neverSuspend) {
186186- await ssrPrepass(React.createElement(AppTree, appTreeProps));
187187- }
188188-189189- return {
190190- ...pageProps,
191191- urqlState: ssrCache ? ssrCache.extractData() : undefined,
192192- urqlClient,
193193- };
194194- };
195195- }
196196-197197- return WithUrql;
198198- };
199199-}