diff --git a/examples/with-apollo-and-redux/components/ErrorMessage.js b/examples/with-apollo-and-redux/components/ErrorMessage.js new file mode 100644 index 00000000..c4a800c7 --- /dev/null +++ b/examples/with-apollo-and-redux/components/ErrorMessage.js @@ -0,0 +1,13 @@ +export default ({message}) => ( + +) diff --git a/examples/with-apollo-and-redux/components/PostList.js b/examples/with-apollo-and-redux/components/PostList.js index 36523648..59efbcfb 100644 --- a/examples/with-apollo-and-redux/components/PostList.js +++ b/examples/with-apollo-and-redux/components/PostList.js @@ -1,9 +1,11 @@ import { gql, graphql } from 'react-apollo' +import ErrorMessage from './ErrorMessage' import PostUpvoter from './PostUpvoter' const POSTS_PER_PAGE = 10 -function PostList ({ data: { allPosts, loading, _allPostsMeta }, loadMorePosts }) { +function PostList ({ data: { loading, error, allPosts, _allPostsMeta }, loadMorePosts }) { + if (error) return if (allPosts && allPosts.length) { const areMorePosts = allPosts.length < _allPostsMeta.count return ( diff --git a/examples/with-apollo-and-redux/lib/withData.js b/examples/with-apollo-and-redux/lib/withData.js index 0940a099..98fb8748 100644 --- a/examples/with-apollo-and-redux/lib/withData.js +++ b/examples/with-apollo-and-redux/lib/withData.js @@ -5,9 +5,14 @@ import Head from 'next/head' import initApollo from './initApollo' import initRedux from './initRedux' +// Gets the display name of a JSX component for dev tools +function getComponentDisplayName (Component) { + return Component.displayName || Component.name || 'Unknown' +} + export default ComposedComponent => { return class WithData extends React.Component { - static displayName = `WithData(${ComposedComponent.displayName})` + static displayName = `WithData(${getComponentDisplayName(ComposedComponent)})` static propTypes = { serverState: PropTypes.object.isRequired } @@ -21,23 +26,28 @@ export default ComposedComponent => { composedInitialProps = await ComposedComponent.getInitialProps(ctx) } - // Run all graphql queries in the component tree + // 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 + // 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 = ( - // No need to use the Redux Provider - // because Apollo sets up the store for us - - - - ) - await getDataFromTree(app) + try { + // Run all GraphQL queries + await getDataFromTree( + // No need to use the Redux Provider + // because Apollo sets up the store for us + + + + ) + } catch (error) { + // Prevent Apollo Client GraphQL errors from crashing SSR. + // Handle them in components via the data.error prop: + // http://dev.apollodata.com/react/api-queries.html#graphql-query-data-error + } // getDataFromTree does not call componentWillUnmount // head side effect therefore need to be cleared manually Head.rewind() @@ -48,7 +58,7 @@ export default ComposedComponent => { // 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 + apollo: { // Only include the Apollo data state data: state.apollo.data } } diff --git a/examples/with-apollo/components/ErrorMessage.js b/examples/with-apollo/components/ErrorMessage.js new file mode 100644 index 00000000..c4a800c7 --- /dev/null +++ b/examples/with-apollo/components/ErrorMessage.js @@ -0,0 +1,13 @@ +export default ({message}) => ( + +) diff --git a/examples/with-apollo/components/PostList.js b/examples/with-apollo/components/PostList.js index 36523648..59efbcfb 100644 --- a/examples/with-apollo/components/PostList.js +++ b/examples/with-apollo/components/PostList.js @@ -1,9 +1,11 @@ import { gql, graphql } from 'react-apollo' +import ErrorMessage from './ErrorMessage' import PostUpvoter from './PostUpvoter' const POSTS_PER_PAGE = 10 -function PostList ({ data: { allPosts, loading, _allPostsMeta }, loadMorePosts }) { +function PostList ({ data: { loading, error, allPosts, _allPostsMeta }, loadMorePosts }) { + if (error) return if (allPosts && allPosts.length) { const areMorePosts = allPosts.length < _allPostsMeta.count return ( diff --git a/examples/with-apollo/lib/withData.js b/examples/with-apollo/lib/withData.js index b54e48cd..47207902 100644 --- a/examples/with-apollo/lib/withData.js +++ b/examples/with-apollo/lib/withData.js @@ -4,9 +4,14 @@ import { ApolloProvider, getDataFromTree } from 'react-apollo' import Head from 'next/head' import initApollo from './initApollo' +// Gets the display name of a JSX component for dev tools +function getComponentDisplayName (Component) { + return Component.displayName || Component.name || 'Unknown' +} + export default ComposedComponent => { return class WithData extends React.Component { - static displayName = `WithData(${ComposedComponent.displayName})` + static displayName = `WithData(${getComponentDisplayName(ComposedComponent)})` static propTypes = { serverState: PropTypes.object.isRequired } @@ -20,29 +25,34 @@ export default ComposedComponent => { composedInitialProps = await ComposedComponent.getInitialProps(ctx) } - // Run all graphql queries in the component tree + // 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 + // 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 = ( - - - - ) - await getDataFromTree(app) + try { + // Run all GraphQL queries + await getDataFromTree( + + + + ) + } catch (error) { + // Prevent Apollo Client GraphQL errors from crashing SSR. + // Handle them in components via the data.error prop: + // http://dev.apollodata.com/react/api-queries.html#graphql-query-data-error + } // getDataFromTree does not call componentWillUnmount // head side effect therefore need to be cleared manually Head.rewind() - // Extract query data from the Apollo's store + // Extract query data from the Apollo store const state = apollo.getInitialState() serverState = { - apollo: { // Make sure to only include Apollo's data state + apollo: { + // Only include the Apollo data state data: state.data } } diff --git a/examples/with-emotion/.babelrc b/examples/with-emotion/.babelrc new file mode 100644 index 00000000..d15ed22b --- /dev/null +++ b/examples/with-emotion/.babelrc @@ -0,0 +1,8 @@ +{ + "presets": [ + "next/babel" + ], + "plugins": [ + ["emotion/babel", { "inline": true }] + ] +} \ No newline at end of file diff --git a/examples/with-emotion/README.md b/examples/with-emotion/README.md new file mode 100644 index 00000000..ded4897c --- /dev/null +++ b/examples/with-emotion/README.md @@ -0,0 +1,34 @@ +[![Deploy to now](https://deploy.now.sh/static/button.svg)](https://deploy.now.sh/?repo=https://github.com/zeit/next.js/tree/master/examples/with-emotion) + +# Example app with [emotion](https://github.com/tkh44/emotion) + +## How to use + +Download the example [or clone the repo](https://github.com/zeit/next.js): + +```bash +curl https://codeload.github.com/zeit/next.js/tar.gz/master | tar -xz --strip=2 next.js-master/examples/with-emotion +cd with-emotion +``` + +Install it and run: + +```bash +npm install +npm run dev +``` + +Deploy it to the cloud with [now](https://zeit.co/now) ([download](https://zeit.co/download)) + +```bash +now +``` + +## The idea behind the example + +This example features how to use [emotion](https://github.com/tkh44/emotion) as the styling solution instead of [styled-jsx](https://github.com/zeit/styled-jsx). + +We are creating three `div` elements with custom styles being shared across the elements. The styles includes the use of pseedo-selector and CSS animations. + + +This is based off the with-glamorous example. \ No newline at end of file diff --git a/examples/with-emotion/package.json b/examples/with-emotion/package.json new file mode 100644 index 00000000..54ce5f15 --- /dev/null +++ b/examples/with-emotion/package.json @@ -0,0 +1,16 @@ +{ + "name": "with-emotion", + "version": "1.0.0", + "scripts": { + "dev": "next", + "build": "next build", + "start": "next start" + }, + "dependencies": { + "emotion": "^5.0.0", + "next": "^2.4.6", + "react": "^15.6.1", + "react-dom": "^15.6.1" + }, + "license": "ISC" +} diff --git a/examples/with-emotion/pages/_document.js b/examples/with-emotion/pages/_document.js new file mode 100644 index 00000000..5a6f5123 --- /dev/null +++ b/examples/with-emotion/pages/_document.js @@ -0,0 +1,33 @@ +import Document, { Head, Main, NextScript } from 'next/document' +import { extractCritical } from 'emotion/server' + +export default class MyDocument extends Document { + static getInitialProps ({ renderPage }) { + const page = renderPage() + const styles = extractCritical(page.html) + return { ...page, ...styles } + } + + constructor (props) { + super(props) + const { __NEXT_DATA__, ids } = props + if (ids) { + __NEXT_DATA__.ids = this.props.ids + } + } + + render () { + return ( + + + With Emotion +