mirror of
https://github.com/terribleplan/next.js.git
synced 2024-01-19 02:48:18 +00:00
[with-apollo] Fix warning about missing _allPostsMeta and more (#3397)
* Fix coding style * Fix className type * Upgrade deps * Fix coding style of lib/ * Simplify onSubmit handler * Fix missing missing _allPostsMeta warning * Follow lint rules
This commit is contained in:
parent
25005d158b
commit
48ed89f93d
|
@ -3,14 +3,16 @@ export default ({ children }) => (
|
|||
{children}
|
||||
<style jsx global>{`
|
||||
* {
|
||||
font-family: Menlo, Monaco, "Lucida Console", "Liberation Mono", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Courier New", monospace, serif;
|
||||
font-family: Menlo, Monaco, 'Lucida Console', 'Liberation Mono',
|
||||
'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New',
|
||||
monospace, serif;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 25px 50px;
|
||||
}
|
||||
a {
|
||||
color: #22BAD9;
|
||||
color: #22bad9;
|
||||
}
|
||||
p {
|
||||
font-size: 14px;
|
||||
|
@ -22,15 +24,15 @@ export default ({ children }) => (
|
|||
}
|
||||
button {
|
||||
align-items: center;
|
||||
background-color: #22BAD9;
|
||||
background-color: #22bad9;
|
||||
border: 0;
|
||||
color: white;
|
||||
display: flex;
|
||||
padding: 5px 7px;
|
||||
}
|
||||
button:active {
|
||||
background-color: #1B9DB7;
|
||||
transition: background-color .3s
|
||||
background-color: #1b9db7;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
button:focus {
|
||||
outline: none;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export default ({message}) => (
|
||||
export default ({ message }) => (
|
||||
<aside>
|
||||
{message}
|
||||
<style jsx>{`
|
||||
|
|
|
@ -3,13 +3,11 @@ import Link from 'next/link'
|
|||
export default ({ pathname }) => (
|
||||
<header>
|
||||
<Link prefetch href='/'>
|
||||
<a className={pathname === '/' && 'is-active'}>Home</a>
|
||||
<a className={pathname === '/' ? 'is-active' : ''}>Home</a>
|
||||
</Link>
|
||||
|
||||
<Link prefetch href='/about'>
|
||||
<a className={pathname === '/about' && 'is-active'}>About</a>
|
||||
<a className={pathname === '/about' ? 'is-active' : ''}>About</a>
|
||||
</Link>
|
||||
|
||||
<style jsx>{`
|
||||
header {
|
||||
margin-bottom: 25px;
|
||||
|
|
|
@ -5,14 +5,17 @@ import PostUpvoter from './PostUpvoter'
|
|||
|
||||
const POSTS_PER_PAGE = 10
|
||||
|
||||
function PostList ({ data: { loading, error, allPosts, _allPostsMeta }, loadMorePosts }) {
|
||||
function PostList ({
|
||||
data: { loading, error, allPosts, _allPostsMeta },
|
||||
loadMorePosts
|
||||
}) {
|
||||
if (error) return <ErrorMessage message='Error loading posts.' />
|
||||
if (allPosts && allPosts.length) {
|
||||
const areMorePosts = allPosts.length < _allPostsMeta.count
|
||||
return (
|
||||
<section>
|
||||
<ul>
|
||||
{allPosts.map((post, index) =>
|
||||
{allPosts.map((post, index) => (
|
||||
<li key={post.id}>
|
||||
<div>
|
||||
<span>{index + 1}. </span>
|
||||
|
@ -20,9 +23,16 @@ function PostList ({ data: { loading, error, allPosts, _allPostsMeta }, loadMore
|
|||
<PostUpvoter id={post.id} votes={post.votes} />
|
||||
</div>
|
||||
</li>
|
||||
)}
|
||||
))}
|
||||
</ul>
|
||||
{areMorePosts ? <button onClick={() => loadMorePosts()}> {loading ? 'Loading...' : 'Show More'} </button> : ''}
|
||||
{areMorePosts ? (
|
||||
<button onClick={() => loadMorePosts()}>
|
||||
{' '}
|
||||
{loading ? 'Loading...' : 'Show More'}{' '}
|
||||
</button>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
<style jsx>{`
|
||||
section {
|
||||
padding-bottom: 20px;
|
||||
|
@ -55,7 +65,7 @@ function PostList ({ data: { loading, error, allPosts, _allPostsMeta }, loadMore
|
|||
border-style: solid;
|
||||
border-width: 6px 4px 0 4px;
|
||||
border-color: #ffffff transparent transparent transparent;
|
||||
content: "";
|
||||
content: '';
|
||||
height: 0;
|
||||
margin-right: 5px;
|
||||
width: 0;
|
||||
|
@ -75,7 +85,7 @@ export const allPosts = gql`
|
|||
votes
|
||||
url
|
||||
createdAt
|
||||
},
|
||||
}
|
||||
_allPostsMeta {
|
||||
count
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ function PostUpvoter ({ upvote, votes, id }) {
|
|||
border-color: transparent transparent #000000 transparent;
|
||||
border-style: solid;
|
||||
border-width: 0 4px 6px 4px;
|
||||
content: "";
|
||||
content: '';
|
||||
height: 0;
|
||||
margin-right: 5px;
|
||||
width: 0;
|
||||
|
@ -42,16 +42,17 @@ const upvotePost = gql`
|
|||
|
||||
export default graphql(upvotePost, {
|
||||
props: ({ ownProps, mutate }) => ({
|
||||
upvote: (id, votes) => mutate({
|
||||
variables: { id, votes },
|
||||
optimisticResponse: {
|
||||
__typename: 'Mutation',
|
||||
updatePost: {
|
||||
__typename: 'Post',
|
||||
id: ownProps.id,
|
||||
votes: ownProps.votes + 1
|
||||
upvote: (id, votes) =>
|
||||
mutate({
|
||||
variables: { id, votes },
|
||||
optimisticResponse: {
|
||||
__typename: 'Mutation',
|
||||
updatePost: {
|
||||
__typename: 'Post',
|
||||
id: ownProps.id,
|
||||
votes: ownProps.votes + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
})(PostUpvoter)
|
||||
|
|
|
@ -1,36 +1,24 @@
|
|||
import { graphql } from 'react-apollo'
|
||||
import gql from 'graphql-tag'
|
||||
import {allPosts, allPostsQueryVars} from './PostList'
|
||||
import { allPosts, allPostsQueryVars } from './PostList'
|
||||
|
||||
function Submit ({ createPost }) {
|
||||
function handleSubmit (e) {
|
||||
e.preventDefault()
|
||||
function handleSubmit (event) {
|
||||
event.preventDefault()
|
||||
|
||||
let title = e.target.elements.title.value
|
||||
let url = e.target.elements.url.value
|
||||
const form = event.target
|
||||
|
||||
if (title === '' || url === '') {
|
||||
window.alert('Both fields are required.')
|
||||
return false
|
||||
}
|
||||
const formData = new window.FormData(form)
|
||||
createPost(formData.get('title'), formData.get('url'))
|
||||
|
||||
// prepend http if missing from url
|
||||
if (!url.match(/^[a-zA-Z]+:\/\//)) {
|
||||
url = `http://${url}`
|
||||
}
|
||||
|
||||
createPost(title, url)
|
||||
|
||||
// reset form
|
||||
e.target.elements.title.value = ''
|
||||
e.target.elements.url.value = ''
|
||||
form.reset()
|
||||
}
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<h1>Submit</h1>
|
||||
<input placeholder='title' name='title' />
|
||||
<input placeholder='url' name='url' />
|
||||
<input placeholder='title' name='title' type='text' required />
|
||||
<input placeholder='url' name='url' type='url' required />
|
||||
<button type='submit'>Submit</button>
|
||||
<style jsx>{`
|
||||
form {
|
||||
|
@ -64,12 +52,23 @@ const createPost = gql`
|
|||
|
||||
export default graphql(createPost, {
|
||||
props: ({ mutate }) => ({
|
||||
createPost: (title, url) => mutate({
|
||||
variables: { title, url },
|
||||
update: (proxy, { data: { createPost } }) => {
|
||||
const data = proxy.readQuery({ query: allPosts, variables: allPostsQueryVars })
|
||||
proxy.writeQuery({ query: allPosts, data: {allPosts: [createPost, ...data.allPosts]}, variables: allPostsQueryVars })
|
||||
}
|
||||
})
|
||||
createPost: (title, url) =>
|
||||
mutate({
|
||||
variables: { title, url },
|
||||
update: (proxy, { data: { createPost } }) => {
|
||||
const data = proxy.readQuery({
|
||||
query: allPosts,
|
||||
variables: allPostsQueryVars
|
||||
})
|
||||
proxy.writeQuery({
|
||||
query: allPosts,
|
||||
data: {
|
||||
...data,
|
||||
allPosts: [createPost, ...data.allPosts]
|
||||
},
|
||||
variables: allPostsQueryVars
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
})(Submit)
|
||||
|
|
|
@ -10,7 +10,7 @@ if (!process.browser) {
|
|||
global.fetch = fetch
|
||||
}
|
||||
|
||||
function create (initialState) {
|
||||
function create(initialState) {
|
||||
return new ApolloClient({
|
||||
connectToDevTools: process.browser,
|
||||
ssrMode: !process.browser, // Disables forceFetch on the server (so queries are only run once)
|
||||
|
@ -18,11 +18,11 @@ function create (initialState) {
|
|||
uri: 'https://api.graph.cool/simple/v1/cixmkt2ul01q00122mksg82pn', // Server URL (must be absolute)
|
||||
credentials: 'same-origin' // Additional fetch() options like `credentials` or `headers`
|
||||
}),
|
||||
cache: new InMemoryCache().restore(initialState || {}),
|
||||
cache: new InMemoryCache().restore(initialState || {})
|
||||
})
|
||||
}
|
||||
|
||||
export default function initApollo (initialState) {
|
||||
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) {
|
||||
|
|
|
@ -5,23 +5,25 @@ import Head from 'next/head'
|
|||
import initApollo from './initApollo'
|
||||
|
||||
// Gets the display name of a JSX component for dev tools
|
||||
function getComponentDisplayName (Component) {
|
||||
function getComponentDisplayName(Component) {
|
||||
return Component.displayName || Component.name || 'Unknown'
|
||||
}
|
||||
|
||||
export default ComposedComponent => {
|
||||
return class WithData extends React.Component {
|
||||
static displayName = `WithData(${getComponentDisplayName(ComposedComponent)})`
|
||||
static displayName = `WithData(${getComponentDisplayName(
|
||||
ComposedComponent
|
||||
)})`
|
||||
static propTypes = {
|
||||
serverState: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
static async getInitialProps (ctx) {
|
||||
static async getInitialProps(ctx) {
|
||||
// Initial serverState with apollo (empty)
|
||||
let serverState = {
|
||||
let serverState = {
|
||||
apollo: {
|
||||
data: { }
|
||||
}
|
||||
data: {}
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate the composed component's getInitialProps()
|
||||
|
@ -35,7 +37,7 @@ export default ComposedComponent => {
|
|||
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}
|
||||
const url = { query: ctx.query, pathname: ctx.pathname }
|
||||
try {
|
||||
// Run all GraphQL queries
|
||||
await getDataFromTree(
|
||||
|
@ -66,12 +68,12 @@ export default ComposedComponent => {
|
|||
}
|
||||
}
|
||||
|
||||
constructor (props) {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.apollo = initApollo(this.props.serverState.apollo.data)
|
||||
}
|
||||
|
||||
render () {
|
||||
render() {
|
||||
return (
|
||||
<ApolloProvider client={this.apollo}>
|
||||
<ComposedComponent {...this.props} />
|
||||
|
|
|
@ -7,16 +7,16 @@
|
|||
"start": "next start"
|
||||
},
|
||||
"dependencies": {
|
||||
"apollo-client-preset": "^1.0.1",
|
||||
"graphql": "^0.11.7",
|
||||
"graphql-anywhere": "^3.1.0",
|
||||
"graphql-tag": "^2.5.0",
|
||||
"isomorphic-unfetch": "^2.0.0",
|
||||
"apollo-client-preset": "1.0.4",
|
||||
"graphql": "0.11.7",
|
||||
"graphql-anywhere": "4.0.2",
|
||||
"graphql-tag": "2.5.0",
|
||||
"isomorphic-unfetch": "2.0.0",
|
||||
"next": "latest",
|
||||
"prop-types": "^15.5.8",
|
||||
"react": "^16.0.0",
|
||||
"react-apollo": "^2.0.0",
|
||||
"react-dom": "^16.0.0"
|
||||
"prop-types": "15.6.0",
|
||||
"react": "16.2.0",
|
||||
"react-apollo": "2.0.1",
|
||||
"react-dom": "16.2.0"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
|
|
|
@ -1,22 +1,40 @@
|
|||
import App from '../components/App'
|
||||
import Header from '../components/Header'
|
||||
|
||||
export default (props) => (
|
||||
export default props => (
|
||||
<App>
|
||||
<Header pathname={props.url.pathname} />
|
||||
<article>
|
||||
<h1>The Idea Behind This Example</h1>
|
||||
<p>
|
||||
<a href='http://dev.apollodata.com'>Apollo</a> is a GraphQL client that allows you to easily query the exact data you need from a GraphQL server. In addition to fetching and mutating data, Apollo analyzes your queries and their results to construct a client-side cache of your data, which is kept up to date as further queries and mutations are run, fetching more results from the server.
|
||||
<a href='http://dev.apollodata.com'>Apollo</a> is a GraphQL client that
|
||||
allows you to easily query the exact data you need from a GraphQL
|
||||
server. In addition to fetching and mutating data, Apollo analyzes your
|
||||
queries and their results to construct a client-side cache of your data,
|
||||
which is kept up to date as further queries and mutations are run,
|
||||
fetching more results from the server.
|
||||
</p>
|
||||
<p>
|
||||
In this simple example, we integrate Apollo seamlessly with <a href='https://github.com/zeit/next.js'>Next</a> by wrapping our pages inside a <a href='https://facebook.github.io/react/docs/higher-order-components.html'>higher-order component (HOC)</a>. Using the HOC pattern we're able to pass down a central store of query result data created by Apollo into our React component hierarchy defined inside each page of our Next application.
|
||||
In this simple example, we integrate Apollo seamlessly with{' '}
|
||||
<a href='https://github.com/zeit/next.js'>Next</a> by wrapping our pages
|
||||
inside a{' '}
|
||||
<a href='https://facebook.github.io/react/docs/higher-order-components.html'>
|
||||
higher-order component (HOC)
|
||||
</a>. Using the HOC pattern we're able to pass down a central store of
|
||||
query result data created by Apollo into our React component hierarchy
|
||||
defined inside each page of our Next application.
|
||||
</p>
|
||||
<p>
|
||||
On initial page load, while on the server and inside getInitialProps, we invoke the Apollo method, <a href='http://dev.apollodata.com/react/server-side-rendering.html#getDataFromTree'>getDataFromTree</a>. This method returns a promise; at the point in which the promise resolves, our Apollo Client store is completely initialized.
|
||||
On initial page load, while on the server and inside getInitialProps, we
|
||||
invoke the Apollo method,{' '}
|
||||
<a href='http://dev.apollodata.com/react/server-side-rendering.html#getDataFromTree'>
|
||||
getDataFromTree
|
||||
</a>. This method returns a promise; at the point in which the promise
|
||||
resolves, our Apollo Client store is completely initialized.
|
||||
</p>
|
||||
<p>
|
||||
This example relies on <a href='http://graph.cool'>graph.cool</a> for its GraphQL backend.
|
||||
This example relies on <a href='http://graph.cool'>graph.cool</a> for
|
||||
its GraphQL backend.
|
||||
</p>
|
||||
</article>
|
||||
</App>
|
||||
|
|
|
@ -4,7 +4,7 @@ import Submit from '../components/Submit'
|
|||
import PostList from '../components/PostList'
|
||||
import withData from '../lib/withData'
|
||||
|
||||
export default withData((props) => (
|
||||
export default withData(props => (
|
||||
<App>
|
||||
<Header pathname={props.url.pathname} />
|
||||
<Submit />
|
||||
|
|
Loading…
Reference in a new issue