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 PropTypes from 'prop-types'
|
||||
import { ApolloProvider, getDataFromTree } from 'react-apollo'
|
||||
import { initClient } from './initClient'
|
||||
import { initStore } from './initStore'
|
||||
import initApollo from './initApollo'
|
||||
import initRedux from './initRedux'
|
||||
|
||||
export default ComposedComponent => {
|
||||
return class WithData extends React.Component {
|
||||
static displayName = `WithData(${ComposedComponent.displayName})`
|
||||
static propTypes = {
|
||||
serverState: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
export default (Component) => (
|
||||
class extends React.Component {
|
||||
static async getInitialProps (ctx) {
|
||||
const headers = ctx.req ? ctx.req.headers : {}
|
||||
const client = initClient(headers)
|
||||
const store = initStore(client, client.initialState)
|
||||
let serverState = {}
|
||||
|
||||
const props = {
|
||||
url: { query: ctx.query, pathname: ctx.pathname },
|
||||
...await (Component.getInitialProps ? Component.getInitialProps(ctx) : {})
|
||||
// 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) {
|
||||
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 = (
|
||||
<ApolloProvider client={client} store={store}>
|
||||
<Component {...props} />
|
||||
// No need to use the Redux Provider
|
||||
// because Apollo sets up the store for us
|
||||
<ApolloProvider client={apollo} store={redux}>
|
||||
<ComposedComponent url={url} {...composedInitialProps} />
|
||||
</ApolloProvider>
|
||||
)
|
||||
await getDataFromTree(app)
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const state = store.getState()
|
||||
|
||||
return {
|
||||
initialState: {
|
||||
...state,
|
||||
apollo: {
|
||||
data: client.getInitialState().data
|
||||
}
|
||||
},
|
||||
headers,
|
||||
...props
|
||||
serverState,
|
||||
...composedInitialProps
|
||||
}
|
||||
}
|
||||
|
||||
constructor (props) {
|
||||
super(props)
|
||||
this.client = initClient(this.props.headers, this.props.initialState)
|
||||
this.store = initStore(this.client, this.props.initialState)
|
||||
this.apollo = initApollo()
|
||||
this.redux = initRedux(this.apollo, this.props.serverState)
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<ApolloProvider client={this.client} store={this.store}>
|
||||
<Component {...this.props} />
|
||||
// No need to use the Redux Provider
|
||||
// because Apollo sets up the store for us
|
||||
<ApolloProvider client={this.apollo} store={this.redux}>
|
||||
<ComposedComponent {...this.props} />
|
||||
</ApolloProvider>
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
{
|
||||
"name": "with-apollo-and-redux",
|
||||
"version": "1.0.0",
|
||||
"version": "2.0.0",
|
||||
"scripts": {
|
||||
"dev": "next",
|
||||
"build": "next build",
|
||||
"start": "next start"
|
||||
},
|
||||
"dependencies": {
|
||||
"graphql": "^0.9.1",
|
||||
"graphql": "^0.9.3",
|
||||
"isomorphic-fetch": "^2.2.1",
|
||||
"next": "latest",
|
||||
"react": "^15.4.2",
|
||||
"react-apollo": "^1.0.0-rc.2",
|
||||
"prop-types": "^15.5.8",
|
||||
"react": "^15.5.4",
|
||||
"react-apollo": "^1.1.3",
|
||||
"react-dom": "^15.5.4",
|
||||
"redux": "^3.6.0"
|
||||
},
|
||||
"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 PropTypes from 'prop-types'
|
||||
import { ApolloProvider, getDataFromTree } from 'react-apollo'
|
||||
import { initClient } from './initClient'
|
||||
import initApollo from './initApollo'
|
||||
|
||||
export default ComposedComponent => {
|
||||
return class WithData extends React.Component {
|
||||
static displayName = `WithData(${ComposedComponent.displayName})`
|
||||
static propTypes = {
|
||||
serverState: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
export default (Component) => (
|
||||
class extends React.Component {
|
||||
static async getInitialProps (ctx) {
|
||||
const headers = ctx.req ? ctx.req.headers : {}
|
||||
const client = initClient(headers)
|
||||
let serverState = {}
|
||||
|
||||
const props = {
|
||||
url: { query: ctx.query, pathname: ctx.pathname },
|
||||
...await (Component.getInitialProps ? Component.getInitialProps(ctx) : {})
|
||||
// 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) {
|
||||
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 = (
|
||||
<ApolloProvider client={client}>
|
||||
<Component {...props} />
|
||||
<ApolloProvider client={apollo}>
|
||||
<ComposedComponent url={url} {...composedInitialProps} />
|
||||
</ApolloProvider>
|
||||
)
|
||||
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 {
|
||||
initialState: {
|
||||
apollo: {
|
||||
data: client.getInitialState().data
|
||||
}
|
||||
},
|
||||
headers,
|
||||
...props
|
||||
serverState,
|
||||
...composedInitialProps
|
||||
}
|
||||
}
|
||||
|
||||
constructor (props) {
|
||||
super(props)
|
||||
this.client = initClient(this.props.headers, this.props.initialState)
|
||||
this.apollo = initApollo(this.props.serverState)
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<ApolloProvider client={this.client}>
|
||||
<Component {...this.props} />
|
||||
<ApolloProvider client={this.apollo}>
|
||||
<ComposedComponent {...this.props} />
|
||||
</ApolloProvider>
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
{
|
||||
"name": "with-apollo",
|
||||
"version": "1.0.1",
|
||||
"version": "2.0.0",
|
||||
"scripts": {
|
||||
"dev": "next",
|
||||
"build": "next build",
|
||||
"start": "next start"
|
||||
},
|
||||
"dependencies": {
|
||||
"graphql": "^0.9.1",
|
||||
"graphql": "^0.9.3",
|
||||
"isomorphic-fetch": "^2.2.1",
|
||||
"next": "latest",
|
||||
"react": "^15.4.2",
|
||||
"react-apollo": "^1.0.0-rc.3"
|
||||
"prop-types": "^15.5.8",
|
||||
"react": "^15.5.4",
|
||||
"react-dom": "^15.5.4",
|
||||
"react-apollo": "^1.1.3"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
|
|
Loading…
Reference in a new issue