mirror of
https://github.com/terribleplan/next.js.git
synced 2024-01-19 02:48:18 +00:00
Many improvements to the Apollo examples (#1905)
* Many improvements to the Apollo examples * Use static properties
This commit is contained in:
parent
3b91355c9e
commit
f4d6cbfc19
36
examples/with-apollo-and-redux/lib/initApollo.js
Normal file
36
examples/with-apollo-and-redux/lib/initApollo.js
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import { ApolloClient, createNetworkInterface } from 'react-apollo'
|
||||||
|
import fetch from 'isomorphic-fetch'
|
||||||
|
|
||||||
|
let apolloClient = null
|
||||||
|
|
||||||
|
// Polyfill fetch() on the server (used by apollo-client)
|
||||||
|
if (!process.browser) {
|
||||||
|
global.fetch = fetch
|
||||||
|
}
|
||||||
|
|
||||||
|
function create () {
|
||||||
|
return new ApolloClient({
|
||||||
|
ssrMode: !process.browser, // Disables forceFetch on the server (so queries are only run once)
|
||||||
|
networkInterface: createNetworkInterface({
|
||||||
|
uri: 'https://api.graph.cool/simple/v1/cixmkt2ul01q00122mksg82pn', // Server URL (must be absolute)
|
||||||
|
opts: { // Additional fetch() options like `credentials` or `headers`
|
||||||
|
credentials: 'same-origin'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function initApollo () {
|
||||||
|
// Make sure to create a new client for every server-side request so that data
|
||||||
|
// isn't shared between connections (which would be bad)
|
||||||
|
if (!process.browser) {
|
||||||
|
return create()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reuse client on the client-side
|
||||||
|
if (!apolloClient) {
|
||||||
|
apolloClient = create()
|
||||||
|
}
|
||||||
|
|
||||||
|
return apolloClient
|
||||||
|
}
|
|
@ -1,28 +0,0 @@
|
||||||
import { ApolloClient, createNetworkInterface } from 'react-apollo'
|
|
||||||
|
|
||||||
let apolloClient = null
|
|
||||||
|
|
||||||
function _initClient (headers, initialState) {
|
|
||||||
return new ApolloClient({
|
|
||||||
initialState,
|
|
||||||
ssrMode: !process.browser,
|
|
||||||
dataIdFromObject: result => result.id || null,
|
|
||||||
networkInterface: createNetworkInterface({
|
|
||||||
uri: 'https://api.graph.cool/simple/v1/cixmkt2ul01q00122mksg82pn',
|
|
||||||
opts: {
|
|
||||||
credentials: 'same-origin'
|
|
||||||
// Pass headers here if your graphql server requires them
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export const initClient = (headers, initialState = {}) => {
|
|
||||||
if (!process.browser) {
|
|
||||||
return _initClient(headers, initialState)
|
|
||||||
}
|
|
||||||
if (!apolloClient) {
|
|
||||||
apolloClient = _initClient(headers, initialState)
|
|
||||||
}
|
|
||||||
return apolloClient
|
|
||||||
}
|
|
39
examples/with-apollo-and-redux/lib/initRedux.js
Normal file
39
examples/with-apollo-and-redux/lib/initRedux.js
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import { createStore, combineReducers, applyMiddleware, compose } from 'redux'
|
||||||
|
import reducers from './reducers'
|
||||||
|
|
||||||
|
let reduxStore = null
|
||||||
|
|
||||||
|
// Get the Redux DevTools extension and fallback to a no-op function
|
||||||
|
let devtools = f => f
|
||||||
|
if (process.browser && window.__REDUX_DEVTOOLS_EXTENSION__) {
|
||||||
|
devtools = window.__REDUX_DEVTOOLS_EXTENSION__()
|
||||||
|
}
|
||||||
|
|
||||||
|
function create (apollo, initialState = {}) {
|
||||||
|
return createStore(
|
||||||
|
combineReducers({ // Setup reducers
|
||||||
|
...reducers,
|
||||||
|
apollo: apollo.reducer()
|
||||||
|
}),
|
||||||
|
initialState, // Hydrate the store with server-side data
|
||||||
|
compose(
|
||||||
|
applyMiddleware(apollo.middleware()), // Add additional middleware here
|
||||||
|
devtools
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function initRedux (apollo, initialState) {
|
||||||
|
// Make sure to create a new store for every server-side request so that data
|
||||||
|
// isn't shared between connections (which would be bad)
|
||||||
|
if (!process.browser) {
|
||||||
|
return create(apollo, initialState)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reuse store on the client-side
|
||||||
|
if (!reduxStore) {
|
||||||
|
reduxStore = create(apollo, initialState)
|
||||||
|
}
|
||||||
|
|
||||||
|
return reduxStore
|
||||||
|
}
|
|
@ -1,18 +0,0 @@
|
||||||
import { createStore } from 'redux'
|
|
||||||
import getReducer from './reducer'
|
|
||||||
import createMiddleware from './middleware'
|
|
||||||
|
|
||||||
let reduxStore = null
|
|
||||||
|
|
||||||
export const initStore = (client, initialState) => {
|
|
||||||
let store
|
|
||||||
if (!process.browser || !reduxStore) {
|
|
||||||
const middleware = createMiddleware(client.middleware())
|
|
||||||
store = createStore(getReducer(client), initialState, middleware)
|
|
||||||
if (!process.browser) {
|
|
||||||
return store
|
|
||||||
}
|
|
||||||
reduxStore = store
|
|
||||||
}
|
|
||||||
return reduxStore
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
import { applyMiddleware, compose } from 'redux'
|
|
||||||
|
|
||||||
export default function createMiddleware (clientMiddleware) {
|
|
||||||
const middleware = applyMiddleware(clientMiddleware)
|
|
||||||
if (process.browser && window.devToolsExtension) {
|
|
||||||
return compose(middleware, window.devToolsExtension())
|
|
||||||
}
|
|
||||||
return middleware
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
import { combineReducers } from 'redux'
|
|
||||||
|
|
||||||
export default function getReducer (client) {
|
|
||||||
return combineReducers({
|
|
||||||
apollo: client.reducer()
|
|
||||||
})
|
|
||||||
}
|
|
12
examples/with-apollo-and-redux/lib/reducers.js
Normal file
12
examples/with-apollo-and-redux/lib/reducers.js
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
export default {
|
||||||
|
example: (state = {}, { type, payload }) => {
|
||||||
|
switch (type) {
|
||||||
|
case 'EXAMPLE_ACTION':
|
||||||
|
return {
|
||||||
|
...state
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,56 +1,75 @@
|
||||||
import 'isomorphic-fetch'
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
import { ApolloProvider, getDataFromTree } from 'react-apollo'
|
import { ApolloProvider, getDataFromTree } from 'react-apollo'
|
||||||
import { initClient } from './initClient'
|
import initApollo from './initApollo'
|
||||||
import { initStore } from './initStore'
|
import initRedux from './initRedux'
|
||||||
|
|
||||||
export default (Component) => (
|
export default ComposedComponent => {
|
||||||
class extends React.Component {
|
return class WithData extends React.Component {
|
||||||
static async getInitialProps (ctx) {
|
static displayName = `WithData(${ComposedComponent.displayName})`
|
||||||
const headers = ctx.req ? ctx.req.headers : {}
|
static propTypes = {
|
||||||
const client = initClient(headers)
|
serverState: PropTypes.object.isRequired
|
||||||
const store = initStore(client, client.initialState)
|
|
||||||
|
|
||||||
const props = {
|
|
||||||
url: { query: ctx.query, pathname: ctx.pathname },
|
|
||||||
...await (Component.getInitialProps ? Component.getInitialProps(ctx) : {})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async getInitialProps (ctx) {
|
||||||
|
let serverState = {}
|
||||||
|
|
||||||
|
// Evaluate the composed component's getInitialProps()
|
||||||
|
let composedInitialProps = {}
|
||||||
|
if (ComposedComponent.getInitialProps) {
|
||||||
|
composedInitialProps = await ComposedComponent.getInitialProps(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run all graphql queries in the component tree
|
||||||
|
// and extract the resulting data
|
||||||
if (!process.browser) {
|
if (!process.browser) {
|
||||||
|
const apollo = initApollo()
|
||||||
|
const redux = initRedux(apollo)
|
||||||
|
// Provide the `url` prop data in case a graphql query uses it
|
||||||
|
const url = {query: ctx.query, pathname: ctx.pathname}
|
||||||
|
|
||||||
|
// Run all graphql queries
|
||||||
const app = (
|
const app = (
|
||||||
<ApolloProvider client={client} store={store}>
|
// No need to use the Redux Provider
|
||||||
<Component {...props} />
|
// because Apollo sets up the store for us
|
||||||
|
<ApolloProvider client={apollo} store={redux}>
|
||||||
|
<ComposedComponent url={url} {...composedInitialProps} />
|
||||||
</ApolloProvider>
|
</ApolloProvider>
|
||||||
)
|
)
|
||||||
await getDataFromTree(app)
|
await getDataFromTree(app)
|
||||||
}
|
|
||||||
|
|
||||||
const state = store.getState()
|
// Extract query data from the store
|
||||||
|
const state = redux.getState()
|
||||||
|
|
||||||
|
// No need to include other initial Redux state because when it
|
||||||
|
// initialises on the client-side it'll create it again anyway
|
||||||
|
serverState = {
|
||||||
|
apollo: { // Make sure to only include Apollo's data state
|
||||||
|
data: state.apollo.data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
initialState: {
|
serverState,
|
||||||
...state,
|
...composedInitialProps
|
||||||
apollo: {
|
|
||||||
data: client.getInitialState().data
|
|
||||||
}
|
|
||||||
},
|
|
||||||
headers,
|
|
||||||
...props
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor (props) {
|
constructor (props) {
|
||||||
super(props)
|
super(props)
|
||||||
this.client = initClient(this.props.headers, this.props.initialState)
|
this.apollo = initApollo()
|
||||||
this.store = initStore(this.client, this.props.initialState)
|
this.redux = initRedux(this.apollo, this.props.serverState)
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
return (
|
return (
|
||||||
<ApolloProvider client={this.client} store={this.store}>
|
// No need to use the Redux Provider
|
||||||
<Component {...this.props} />
|
// because Apollo sets up the store for us
|
||||||
|
<ApolloProvider client={this.apollo} store={this.redux}>
|
||||||
|
<ComposedComponent {...this.props} />
|
||||||
</ApolloProvider>
|
</ApolloProvider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
|
|
|
@ -1,16 +1,19 @@
|
||||||
{
|
{
|
||||||
"name": "with-apollo-and-redux",
|
"name": "with-apollo-and-redux",
|
||||||
"version": "1.0.0",
|
"version": "2.0.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next",
|
"dev": "next",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start"
|
"start": "next start"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"graphql": "^0.9.1",
|
"graphql": "^0.9.3",
|
||||||
|
"isomorphic-fetch": "^2.2.1",
|
||||||
"next": "latest",
|
"next": "latest",
|
||||||
"react": "^15.4.2",
|
"prop-types": "^15.5.8",
|
||||||
"react-apollo": "^1.0.0-rc.2",
|
"react": "^15.5.4",
|
||||||
|
"react-apollo": "^1.1.3",
|
||||||
|
"react-dom": "^15.5.4",
|
||||||
"redux": "^3.6.0"
|
"redux": "^3.6.0"
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
|
|
37
examples/with-apollo/lib/initApollo.js
Normal file
37
examples/with-apollo/lib/initApollo.js
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import { ApolloClient, createNetworkInterface } from 'react-apollo'
|
||||||
|
import fetch from 'isomorphic-fetch'
|
||||||
|
|
||||||
|
let apolloClient = null
|
||||||
|
|
||||||
|
// Polyfill fetch() on the server (used by apollo-client)
|
||||||
|
if (!process.browser) {
|
||||||
|
global.fetch = fetch
|
||||||
|
}
|
||||||
|
|
||||||
|
function create (initialState) {
|
||||||
|
return new ApolloClient({
|
||||||
|
initialState,
|
||||||
|
ssrMode: !process.browser, // Disables forceFetch on the server (so queries are only run once)
|
||||||
|
networkInterface: createNetworkInterface({
|
||||||
|
uri: 'https://api.graph.cool/simple/v1/cixmkt2ul01q00122mksg82pn', // Server URL (must be absolute)
|
||||||
|
opts: { // Additional fetch() options like `credentials` or `headers`
|
||||||
|
credentials: 'same-origin'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function initApollo (initialState) {
|
||||||
|
// Make sure to create a new client for every server-side request so that data
|
||||||
|
// isn't shared between connections (which would be bad)
|
||||||
|
if (!process.browser) {
|
||||||
|
return create(initialState)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reuse client on the client-side
|
||||||
|
if (!apolloClient) {
|
||||||
|
apolloClient = create(initialState)
|
||||||
|
}
|
||||||
|
|
||||||
|
return apolloClient
|
||||||
|
}
|
|
@ -1,28 +0,0 @@
|
||||||
import { ApolloClient, createNetworkInterface } from 'react-apollo'
|
|
||||||
|
|
||||||
let apolloClient = null
|
|
||||||
|
|
||||||
function _initClient (headers, initialState) {
|
|
||||||
return new ApolloClient({
|
|
||||||
initialState,
|
|
||||||
ssrMode: !process.browser,
|
|
||||||
dataIdFromObject: result => result.id || null,
|
|
||||||
networkInterface: createNetworkInterface({
|
|
||||||
uri: 'https://api.graph.cool/simple/v1/cixmkt2ul01q00122mksg82pn',
|
|
||||||
opts: {
|
|
||||||
credentials: 'same-origin'
|
|
||||||
// Pass headers here if your graphql server requires them
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export const initClient = (headers, initialState = {}) => {
|
|
||||||
if (!process.browser) {
|
|
||||||
return _initClient(headers, initialState)
|
|
||||||
}
|
|
||||||
if (!apolloClient) {
|
|
||||||
apolloClient = _initClient(headers, initialState)
|
|
||||||
}
|
|
||||||
return apolloClient
|
|
||||||
}
|
|
|
@ -1,50 +1,66 @@
|
||||||
import 'isomorphic-fetch'
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
import { ApolloProvider, getDataFromTree } from 'react-apollo'
|
import { ApolloProvider, getDataFromTree } from 'react-apollo'
|
||||||
import { initClient } from './initClient'
|
import initApollo from './initApollo'
|
||||||
|
|
||||||
export default (Component) => (
|
export default ComposedComponent => {
|
||||||
class extends React.Component {
|
return class WithData extends React.Component {
|
||||||
static async getInitialProps (ctx) {
|
static displayName = `WithData(${ComposedComponent.displayName})`
|
||||||
const headers = ctx.req ? ctx.req.headers : {}
|
static propTypes = {
|
||||||
const client = initClient(headers)
|
serverState: PropTypes.object.isRequired
|
||||||
|
|
||||||
const props = {
|
|
||||||
url: { query: ctx.query, pathname: ctx.pathname },
|
|
||||||
...await (Component.getInitialProps ? Component.getInitialProps(ctx) : {})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async getInitialProps (ctx) {
|
||||||
|
let serverState = {}
|
||||||
|
|
||||||
|
// Evaluate the composed component's getInitialProps()
|
||||||
|
let composedInitialProps = {}
|
||||||
|
if (ComposedComponent.getInitialProps) {
|
||||||
|
composedInitialProps = await ComposedComponent.getInitialProps(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run all graphql queries in the component tree
|
||||||
|
// and extract the resulting data
|
||||||
if (!process.browser) {
|
if (!process.browser) {
|
||||||
|
const apollo = initApollo()
|
||||||
|
// Provide the `url` prop data in case a graphql query uses it
|
||||||
|
const url = {query: ctx.query, pathname: ctx.pathname}
|
||||||
|
|
||||||
|
// Run all graphql queries
|
||||||
const app = (
|
const app = (
|
||||||
<ApolloProvider client={client}>
|
<ApolloProvider client={apollo}>
|
||||||
<Component {...props} />
|
<ComposedComponent url={url} {...composedInitialProps} />
|
||||||
</ApolloProvider>
|
</ApolloProvider>
|
||||||
)
|
)
|
||||||
await getDataFromTree(app)
|
await getDataFromTree(app)
|
||||||
|
|
||||||
|
// Extract query data from the Apollo's store
|
||||||
|
const state = apollo.getInitialState()
|
||||||
|
|
||||||
|
serverState = {
|
||||||
|
apollo: { // Make sure to only include Apollo's data state
|
||||||
|
data: state.data
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
initialState: {
|
serverState,
|
||||||
apollo: {
|
...composedInitialProps
|
||||||
data: client.getInitialState().data
|
|
||||||
}
|
|
||||||
},
|
|
||||||
headers,
|
|
||||||
...props
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor (props) {
|
constructor (props) {
|
||||||
super(props)
|
super(props)
|
||||||
this.client = initClient(this.props.headers, this.props.initialState)
|
this.apollo = initApollo(this.props.serverState)
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
return (
|
return (
|
||||||
<ApolloProvider client={this.client}>
|
<ApolloProvider client={this.apollo}>
|
||||||
<Component {...this.props} />
|
<ComposedComponent {...this.props} />
|
||||||
</ApolloProvider>
|
</ApolloProvider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
|
|
|
@ -1,16 +1,19 @@
|
||||||
{
|
{
|
||||||
"name": "with-apollo",
|
"name": "with-apollo",
|
||||||
"version": "1.0.1",
|
"version": "2.0.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next",
|
"dev": "next",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start"
|
"start": "next start"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"graphql": "^0.9.1",
|
"graphql": "^0.9.3",
|
||||||
|
"isomorphic-fetch": "^2.2.1",
|
||||||
"next": "latest",
|
"next": "latest",
|
||||||
"react": "^15.4.2",
|
"prop-types": "^15.5.8",
|
||||||
"react-apollo": "^1.0.0-rc.3"
|
"react": "^15.5.4",
|
||||||
|
"react-dom": "^15.5.4",
|
||||||
|
"react-apollo": "^1.1.3"
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
|
|
Loading…
Reference in a new issue