This repository has been archived by the owner on Mar 8, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 7
Add better support for _app.js. Move types to separate module. #38
Merged
Merged
Changes from 2 commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,9 @@ | ||
import React from 'react'; | ||
import { withUrqlClient } from 'next-urql'; | ||
import { AppPropsType } from 'next/dist/next-server/lib/utils'; | ||
import { AppProps } from 'next/app'; | ||
|
||
const App: React.FC<AppPropsType> = ({ Component, pageProps }) => ( | ||
<Component {...pageProps} /> | ||
); | ||
const App = ({ Component, pageProps }: AppProps) => { | ||
return <Component {...pageProps} />; | ||
}; | ||
|
||
export default withUrqlClient({ url: 'https://graphql-pokemon.now.sh' })(App); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,30 @@ | ||
import React from 'react'; | ||
import { NextPage } from 'next'; | ||
import Head from 'next/head'; | ||
|
||
import PokémonList from '../components/pokemon_list'; | ||
|
||
const Home: React.FC = () => ( | ||
<div> | ||
<Head> | ||
<title>Home</title> | ||
<link rel="icon" href="/favicon.ico" /> | ||
</Head> | ||
interface InitialProps { | ||
title: string; | ||
} | ||
|
||
<PokémonList /> | ||
</div> | ||
); | ||
const Home: NextPage<InitialProps> = ({ title }) => { | ||
return ( | ||
<div> | ||
<Head> | ||
<title>Home</title> | ||
<link rel="icon" href="/favicon.ico" /> | ||
</Head> | ||
<h1>{title}</h1> | ||
<PokémonList /> | ||
</div> | ||
); | ||
}; | ||
|
||
Home.getInitialProps = () => { | ||
return { | ||
title: 'Pokédex', | ||
}; | ||
}; | ||
|
||
export default Home; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
export { default as withUrqlClient, NextUrqlContext } from './with-urql-client'; | ||
export { withUrqlClient } from './with-urql-client'; | ||
export * from './types'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import { NextPageContext } from 'next'; | ||
import { ClientOptions, Exchange, Client } from 'urql'; | ||
import { SSRExchange, SSRData } from 'urql/dist/types/exchanges/ssr'; | ||
import { AppContext } from 'next/app'; | ||
|
||
export type NextUrqlClientOptions = Omit< | ||
ClientOptions, | ||
'exchanges' | 'suspense' | ||
>; | ||
|
||
export type NextUrqlClientConfig = | ||
| NextUrqlClientOptions | ||
| ((ctx?: NextPageContext) => NextUrqlClientOptions); | ||
|
||
export type MergeExchanges = (ssrExchange: SSRExchange) => Exchange[]; | ||
|
||
export interface NextUrqlPageContext extends NextPageContext { | ||
urqlClient: Client; | ||
} | ||
|
||
export interface NextUrqlAppContext extends AppContext { | ||
urqlClient: Client; | ||
} | ||
|
||
export type NextUrqlContext = NextUrqlPageContext | NextUrqlAppContext; | ||
|
||
export interface WithUrqlState { | ||
urqlState?: SSRData; | ||
} | ||
|
||
export interface WithUrqlClient { | ||
urqlClient: Client; | ||
} | ||
|
||
export interface WithUrqlProps extends WithUrqlClient, WithUrqlState { | ||
[key: string]: any; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,106 +1,75 @@ | ||
import React from 'react'; | ||
import { NextPage, NextPageContext, NextComponentType } from 'next'; | ||
import { AppContext } from 'next/app'; | ||
import { NextPage, NextPageContext } from 'next'; | ||
import NextApp, { AppContext } from 'next/app'; | ||
import ssrPrepass from 'react-ssr-prepass'; | ||
import { | ||
Provider, | ||
Client, | ||
ClientOptions, | ||
dedupExchange, | ||
cacheExchange, | ||
fetchExchange, | ||
Exchange, | ||
} from 'urql'; | ||
import { SSRExchange, SSRData } from 'urql/dist/types/exchanges/ssr'; | ||
import { Provider, dedupExchange, cacheExchange, fetchExchange } from 'urql'; | ||
|
||
import { initUrqlClient } from './init-urql-client'; | ||
|
||
export type NextUrqlClientOptions = Omit< | ||
ClientOptions, | ||
'exchanges' | 'suspense' | ||
>; | ||
|
||
interface WithUrqlClient { | ||
urqlClient?: Client; | ||
} | ||
|
||
interface WithUrqlInitialProps { | ||
urqlState: SSRData; | ||
} | ||
|
||
interface PageProps { | ||
pageProps?: WithUrqlClient; | ||
} | ||
|
||
export interface NextUrqlContext extends NextPageContext { | ||
urqlClient: Client; | ||
import { | ||
NextUrqlClientConfig, | ||
MergeExchanges, | ||
NextUrqlContext, | ||
WithUrqlProps, | ||
} from './types'; | ||
|
||
function getDisplayName(Component: React.ComponentType<any>) { | ||
return Component.displayName || Component.name || 'Component'; | ||
} | ||
|
||
type NextUrqlClientConfig = | ||
| NextUrqlClientOptions | ||
| ((ctx?: NextPageContext) => NextUrqlClientOptions); | ||
|
||
function withUrqlClient<T = any, IP = any>( | ||
export function withUrqlClient( | ||
clientConfig: NextUrqlClientConfig, | ||
mergeExchanges: (ssrEx: SSRExchange) => Exchange[] = ssrEx => [ | ||
mergeExchanges: MergeExchanges = ssrExchange => [ | ||
dedupExchange, | ||
cacheExchange, | ||
ssrEx, | ||
ssrExchange, | ||
fetchExchange, | ||
], | ||
) { | ||
return (Page: NextPage<T & IP & WithUrqlClient, IP>) => { | ||
const withUrql: NextComponentType< | ||
NextUrqlContext, | ||
IP | (IP & WithUrqlInitialProps), | ||
T & IP & WithUrqlClient & WithUrqlInitialProps & PageProps | ||
> = ({ urqlClient, urqlState, pageProps, ...rest }) => { | ||
// The React Hooks ESLint plugin will not interpret withUrql as a React component | ||
// due to the NextComponentType annotation. Ignore the warning about not using useMemo. | ||
|
||
return (AppOrPage: NextPage<any> | typeof NextApp) => { | ||
const withUrql = ({ urqlClient, urqlState, ...rest }: WithUrqlProps) => { | ||
// eslint-disable-next-line react-hooks/rules-of-hooks | ||
const client = React.useMemo(() => { | ||
const clientOptions = | ||
typeof clientConfig === 'function' ? clientConfig() : clientConfig; | ||
|
||
return ( | ||
urqlClient || | ||
pageProps?.urqlClient || | ||
initUrqlClient(clientOptions, mergeExchanges, urqlState)[0] | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
initUrqlClient(clientOptions, mergeExchanges, urqlState)[0]! | ||
); | ||
}, [urqlClient, pageProps, urqlState]) as Client; | ||
}, [urqlClient, urqlState]); | ||
|
||
return ( | ||
<Provider value={client}> | ||
<Page urqlClient={client} {...(rest as T & IP)} /> | ||
<AppOrPage urqlClient={client} {...rest} /> | ||
</Provider> | ||
); | ||
}; | ||
|
||
// Set the displayName to indicate use of withUrqlClient. | ||
const displayName = Page.displayName || Page.name || 'Component'; | ||
withUrql.displayName = `withUrqlClient(${displayName})`; | ||
withUrql.displayName = `withUrqlClient(${getDisplayName(AppOrPage)})`; | ||
|
||
withUrql.getInitialProps = async (ctx: NextPageContext | AppContext) => { | ||
const { AppTree } = ctx; | ||
withUrql.getInitialProps = async (appOrPageCtx: NextUrqlContext) => { | ||
const { AppTree } = appOrPageCtx; | ||
|
||
const appCtx = (ctx as AppContext).ctx; | ||
const isApp = !!appCtx; | ||
// Determine if we are wrapping an App component or a Page component. | ||
const isApp = !!(appOrPageCtx as AppContext).Component; | ||
const ctx = isApp | ||
? (appOrPageCtx as AppContext).ctx | ||
: (appOrPageCtx as NextPageContext); | ||
|
||
const opts = | ||
typeof clientConfig === 'function' | ||
? clientConfig(isApp ? appCtx : (ctx as NextPageContext)) | ||
: clientConfig; | ||
typeof clientConfig === 'function' ? clientConfig(ctx) : clientConfig; | ||
const [urqlClient, ssrCache] = initUrqlClient(opts, mergeExchanges); | ||
|
||
if (urqlClient) { | ||
(ctx as NextUrqlContext).urqlClient = urqlClient; | ||
} | ||
|
||
// Run the wrapped component's getInitialProps function. | ||
let pageProps = {} as IP; | ||
if (Page.getInitialProps) { | ||
pageProps = await Page.getInitialProps(ctx as NextUrqlContext); | ||
let pageProps; | ||
if (AppOrPage.getInitialProps) { | ||
pageProps = await AppOrPage.getInitialProps(appOrPageCtx as any); | ||
} | ||
|
||
// Check the window object to determine whether or not we are on the server. | ||
|
@@ -110,27 +79,18 @@ function withUrqlClient<T = any, IP = any>( | |
return pageProps; | ||
} | ||
|
||
// Run the prepass step on AppTree. This will run all urql queries on the server. | ||
await ssrPrepass( | ||
<AppTree | ||
pageProps={{ | ||
...pageProps, | ||
urqlClient, | ||
}} | ||
/>, | ||
); | ||
const props = { ...pageProps, urqlClient }; | ||
const appTreeProps = isApp ? props : { pageProps: props }; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
// Extract the query data from urql's SSR cache. | ||
const urqlState = ssrCache?.extractData(); | ||
// Run the prepass step on AppTree. This will run all urql queries on the server. | ||
await ssrPrepass(<AppTree {...appTreeProps} />); | ||
|
||
return { | ||
...pageProps, | ||
urqlState, | ||
urqlState: ssrCache?.extractData(), | ||
}; | ||
}; | ||
|
||
return withUrql; | ||
}; | ||
} | ||
|
||
export default withUrqlClient; |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I went for naming this
AppOrPage
– a much better description of what this can be!