提交 cba5c05b 编写于 作者: H Henrik Wenz 提交者: JJ Kasper

Fix getInitialProps issue in with-apollo example (#8620)

* Fix getInitialProps in with-apollo example

The code before had two design flaws:

* When we skip WithApollo.getInitialProps we must hoist PageComponent.getInitialProps if it is present.
* We should expose the apolloClient to underlying PageComponent.getInitialProps contexts.

* Add abort check

* Add some comments

* Add client only example page

https://github.com/zeit/next.js/pull/8620#issuecomment-527870886

* Remove connectToDevTools setting in favor default config

https://github.com/apollographql/apollo-client/blob/7eaf4132cd2cd6244260777799406aaa03fcf377/packages/apollo-client/src/ApolloClient.ts#L170-L173

* Remove fetch check

This is done by https://www.npmjs.com/package/isomorphic-unfetch

* Remove apollo-boost

I am removing this package, because we never actually used it.
This is because we use the named export of apollo boost wich resolves to apollo-client.

This way we removed apollo-link-state, apollo-link-error

* Remove redirect code from the client
上级 2ba352da
...@@ -6,6 +6,11 @@ const Header = ({ router: { pathname } }) => ( ...@@ -6,6 +6,11 @@ const Header = ({ router: { pathname } }) => (
<Link href='/'> <Link href='/'>
<a className={pathname === '/' ? 'is-active' : ''}>Home</a> <a className={pathname === '/' ? 'is-active' : ''}>Home</a>
</Link> </Link>
<Link href='/client-only'>
<a className={pathname === '/client-only' ? 'is-active' : ''}>
Client-Only
</a>
</Link>
<Link href='/about'> <Link href='/about'>
<a className={pathname === '/about' ? 'is-active' : ''}>About</a> <a className={pathname === '/about' ? 'is-active' : ''}>About</a>
</Link> </Link>
......
const InfoBox = ({ children }) => (
<div className='info'>
<style jsx>{`
.info {
margin-top: 20px;
margin-bottom: 20px;
padding-top: 20px;
padding-bottom: 20px;
border-top: 1px solid #ececec;
border-bottom: 1px solid #ececec;
}
`}</style>
{children}
</div>
)
export default InfoBox
import { useQuery } from '@apollo/react-hooks' import { useQuery } from '@apollo/react-hooks'
import { NetworkStatus } from 'apollo-boost' import { NetworkStatus } from 'apollo-client'
import gql from 'graphql-tag' import gql from 'graphql-tag'
import ErrorMessage from './ErrorMessage' import ErrorMessage from './ErrorMessage'
import PostUpvoter from './PostUpvoter' import PostUpvoter from './PostUpvoter'
......
import React, { useMemo } from 'react' import React, { useMemo } from 'react'
import Head from 'next/head' import Head from 'next/head'
import { ApolloProvider } from '@apollo/react-hooks' import { ApolloProvider } from '@apollo/react-hooks'
import { ApolloClient, InMemoryCache, HttpLink } from 'apollo-boost' import { ApolloClient } from 'apollo-client'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { HttpLink } from 'apollo-link-http'
import fetch from 'isomorphic-unfetch' import fetch from 'isomorphic-unfetch'
let apolloClient = null let apolloClient = null
...@@ -39,50 +41,60 @@ export function withApollo (PageComponent, { ssr = true } = {}) { ...@@ -39,50 +41,60 @@ export function withApollo (PageComponent, { ssr = true } = {}) {
WithApollo.displayName = `withApollo(${displayName})` WithApollo.displayName = `withApollo(${displayName})`
} }
// Allow Next.js to remove getInitialProps from the browser build if (ssr || PageComponent.getInitialProps) {
if (typeof window === 'undefined') { WithApollo.getInitialProps = async ctx => {
if (ssr) { const { AppTree } = ctx
WithApollo.getInitialProps = async ctx => {
const { AppTree } = ctx
let pageProps = {} // Initialize ApolloClient, add it to the ctx object so
if (PageComponent.getInitialProps) { // we can use it in `PageComponent.getInitialProp`.
pageProps = await PageComponent.getInitialProps(ctx) const apolloClient = (ctx.apolloClient = initApolloClient())
}
// Run wrapped getInitialProps methods
let pageProps = {}
if (PageComponent.getInitialProps) {
pageProps = await PageComponent.getInitialProps(ctx)
}
// Run all GraphQL queries in the component tree // Only on the server:
// and extract the resulting data if (typeof window === 'undefined') {
const apolloClient = initApolloClient() // When redirecting, the response is finished.
// No point in continuing to render
try { if (ctx.res && ctx.res.finished) {
// Run all GraphQL queries return pageProps
const { getDataFromTree } = await import('@apollo/react-ssr')
await getDataFromTree(
<AppTree
pageProps={{
...pageProps,
apolloClient
}}
/>
)
} catch (error) {
// Prevent Apollo Client GraphQL errors from crashing SSR.
// Handle them in components via the data.error prop:
// https://www.apollographql.com/docs/react/api/react-apollo.html#graphql-query-data-error
console.error('Error while running `getDataFromTree`', error)
} }
// getDataFromTree does not call componentWillUnmount // Only if ssr is enabled
// head side effect therefore need to be cleared manually if (ssr) {
Head.rewind() try {
// Run all GraphQL queries
const { getDataFromTree } = await import('@apollo/react-ssr')
await getDataFromTree(
<AppTree
pageProps={{
...pageProps,
apolloClient
}}
/>
)
} catch (error) {
// Prevent Apollo Client GraphQL errors from crashing SSR.
// Handle them in components via the data.error prop:
// https://www.apollographql.com/docs/react/api/react-apollo.html#graphql-query-data-error
console.error('Error while running `getDataFromTree`', error)
}
// getDataFromTree does not call componentWillUnmount
// head side effect therefore need to be cleared manually
Head.rewind()
}
}
// Extract query data from the Apollo store // Extract query data from the Apollo store
const apolloState = apolloClient.cache.extract() const apolloState = apolloClient.cache.extract()
return { return {
...pageProps, ...pageProps,
apolloState apolloState
}
} }
} }
} }
...@@ -116,15 +128,12 @@ function initApolloClient (initialState) { ...@@ -116,15 +128,12 @@ function initApolloClient (initialState) {
*/ */
function createApolloClient (initialState = {}) { function createApolloClient (initialState = {}) {
// Check out https://github.com/zeit/next.js/pull/4611 if you want to use the AWSAppSyncClient // Check out https://github.com/zeit/next.js/pull/4611 if you want to use the AWSAppSyncClient
const isBrowser = typeof window !== 'undefined'
return new ApolloClient({ return new ApolloClient({
connectToDevTools: isBrowser, ssrMode: typeof window === 'undefined', // Disables forceFetch on the server (so queries are only run once)
ssrMode: !isBrowser, // Disables forceFetch on the server (so queries are only run once)
link: new HttpLink({ link: new HttpLink({
uri: 'https://api.graph.cool/simple/v1/cixmkt2ul01q00122mksg82pn', // Server URL (must be absolute) uri: 'https://api.graph.cool/simple/v1/cixmkt2ul01q00122mksg82pn', // Server URL (must be absolute)
credentials: 'same-origin', // Additional fetch() options like `credentials` or `headers` credentials: 'same-origin', // Additional fetch() options like `credentials` or `headers`
// Use fetch() polyfill on the server fetch
fetch: !isBrowser && fetch
}), }),
cache: new InMemoryCache().restore(initialState) cache: new InMemoryCache().restore(initialState)
}) })
......
...@@ -9,14 +9,16 @@ ...@@ -9,14 +9,16 @@
"dependencies": { "dependencies": {
"@apollo/react-hooks": "3.0.0", "@apollo/react-hooks": "3.0.0",
"@apollo/react-ssr": "3.0.0", "@apollo/react-ssr": "3.0.0",
"apollo-boost": "0.4.4", "apollo-cache-inmemory": "1.6.3",
"apollo-client": "2.6.4",
"apollo-link-http": "1.5.15",
"graphql": "^14.0.2", "graphql": "^14.0.2",
"graphql-tag": "2.10.1",
"isomorphic-unfetch": "^3.0.0", "isomorphic-unfetch": "^3.0.0",
"next": "latest", "next": "latest",
"prop-types": "^15.6.2", "prop-types": "^15.6.2",
"react": "^16.7.0", "react": "^16.7.0",
"react-dom": "^16.7.0" "react-dom": "^16.7.0"
}, },
"author": "",
"license": "ISC" "license": "ISC"
} }
import App from '../components/App'
import InfoBox from '../components/InfoBox'
import Header from '../components/Header'
import Submit from '../components/Submit'
import PostList from '../components/PostList'
import { withApollo } from '../lib/apollo'
const ClientOnlyPage = props => (
<App>
<Header />
<InfoBox>
ℹ️ This example shows how to disable apollos query fetching on the server.
If you <a href='/client-only'>reload</a> this page, you will see a loader
since Apollo didn't fetch any data on the server. This allows{' '}
<a
href='https://nextjs.org/blog/next-9#automatic-static-optimization'
target='_blank'
>
automatic static optimization
</a>
.
</InfoBox>
<Submit />
<PostList />
</App>
)
export default withApollo(ClientOnlyPage, {
// Disable apollo ssr fetching in favour of automatic static optimization
ssr: false
})
import App from '../components/App' import App from '../components/App'
import InfoBox from '../components/InfoBox'
import Header from '../components/Header' import Header from '../components/Header'
import Submit from '../components/Submit' import Submit from '../components/Submit'
import PostList from '../components/PostList' import PostList from '../components/PostList'
...@@ -7,6 +8,18 @@ import { withApollo } from '../lib/apollo' ...@@ -7,6 +8,18 @@ import { withApollo } from '../lib/apollo'
const IndexPage = props => ( const IndexPage = props => (
<App> <App>
<Header /> <Header />
<InfoBox>
ℹ️ This example shows how to fetch all initial apollo queries on the
server. If you <a href='/'>reload</a> this page you won't see a loader
since Apollo fetched all needed data on the server. This prevents{' '}
<a
href='https://nextjs.org/blog/next-9#automatic-static-optimization'
target='_blank'
>
automatic static optimization
</a>{' '}
in favour of full Server-Side-Rendering.
</InfoBox>
<Submit /> <Submit />
<PostList /> <PostList />
</App> </App>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册