Skip to content

Commit c2036e1

Browse files
adamsofferrauchg
authored andcommitted
Create separate Apollo example without Redux integration (vercel#1483)
* Add minimal apollo example * Update apollo example README * Update apollo example demo link in README * Fix button styles * Fix show more button * Alias demo url * Include the data field on the Apollo store when hydrating * Revert * Include the data field on the Apollo store when hydrating per tpreusse's suggestion. * Add example to faq section in README * Sort by newest; Add active state to buttons * Make optimization suggestions * Use process.browser; inline props * Pass wrapped component's initial props into component heirarchy if they exist * Remove unnecessary sorting of array * Update Apollo example * Remove trailing comma * Update reduxRootKey * Remove unnecessary babelrc * Update with-apollo example - Remove use of deprecated 'reduxRootKey' option - Add loading indicator inside pagination button * Fix with-apollo example pagination; Pass initialState to ApolloClient * Split apollo example into two (one with and without Redux integration) * Rename createClient private function to _initClient * Set initialState default parameter inside initClient function * Remove redux dep from with-apollo example
1 parent 102c20d commit c2036e1

File tree

19 files changed

+485
-17
lines changed

19 files changed

+485
-17
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Apollo & Redux Example
2+
3+
## How to use
4+
5+
Download the example [or clone the repo](https://github.com/zeit/next.js):
6+
7+
```bash
8+
curl https://codeload.github.com/zeit/next.js/tar.gz/master | tar -xz --strip=2 next.js-master/examples/with-apollo-and-redux
9+
cd with-apollo-and-redux
10+
```
11+
12+
Install it and run:
13+
14+
```bash
15+
npm install
16+
npm run dev
17+
```
18+
19+
Deploy it to the cloud with [now](https://zeit.co/now) ([download](https://zeit.co/download)):
20+
21+
```bash
22+
now
23+
```
24+
25+
## The idea behind the example
26+
By default, Apollo Client creates its own internal Redux store to manage queries and their results. If you are already using Redux for the rest of your app, [you can have the client integrate with your existing store instead](http://dev.apollodata.com/react/redux.html). This example is identical to the [`with-apollo`](https://github.com/zeit/next.js/tree/master/examples/with-apollo) with the exception of this Redux store integration.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
export default ({ children }) => (
2+
<main>
3+
{children}
4+
<style jsx global>{`
5+
* {
6+
font-family: Menlo, Monaco, "Lucida Console", "Liberation Mono", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Courier New", monospace, serif;
7+
}
8+
body {
9+
margin: 0;
10+
padding: 25px 50px;
11+
}
12+
a {
13+
color: #22BAD9;
14+
}
15+
p {
16+
font-size: 14px;
17+
line-height: 24px;
18+
}
19+
article {
20+
margin: 0 auto;
21+
max-width: 650px;
22+
}
23+
button {
24+
align-items: center;
25+
background-color: #22BAD9;
26+
border: 0;
27+
color: white;
28+
display: flex;
29+
padding: 5px 7px;
30+
}
31+
button:active {
32+
background-color: #1B9DB7;
33+
transition: background-color .3s
34+
}
35+
button:focus {
36+
outline: none;
37+
}
38+
`}</style>
39+
</main>
40+
)
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import Link from 'next/link'
2+
3+
export default ({ pathname }) => (
4+
<header>
5+
<Link prefetch href='/'>
6+
<a className={pathname === '/' && 'is-active'}>Home</a>
7+
</Link>
8+
9+
<Link prefetch href='/about'>
10+
<a className={pathname === '/about' && 'is-active'}>About</a>
11+
</Link>
12+
13+
<style jsx>{`
14+
header {
15+
margin-bottom: 25px;
16+
}
17+
a {
18+
font-size: 14px;
19+
margin-right: 15px;
20+
text-decoration: none;
21+
}
22+
.is-active {
23+
text-decoration: underline;
24+
}
25+
`}</style>
26+
</header>
27+
)
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import { gql, graphql } from 'react-apollo'
2+
import PostUpvoter from './PostUpvoter'
3+
4+
const POSTS_PER_PAGE = 10
5+
6+
function PostList ({ data: { allPosts, loading, _allPostsMeta }, loadMorePosts }) {
7+
if (allPosts && allPosts.length) {
8+
const areMorePosts = allPosts.length < _allPostsMeta.count
9+
return (
10+
<section>
11+
<ul>
12+
{allPosts.map((post, index) =>
13+
<li key={post.id}>
14+
<div>
15+
<span>{index + 1}. </span>
16+
<a href={post.url}>{post.title}</a>
17+
<PostUpvoter id={post.id} votes={post.votes} />
18+
</div>
19+
</li>
20+
)}
21+
</ul>
22+
{areMorePosts ? <button onClick={() => loadMorePosts()}> {loading ? 'Loading...' : 'Show More'} </button> : ''}
23+
<style jsx>{`
24+
section {
25+
padding-bottom: 20px;
26+
}
27+
li {
28+
display: block;
29+
margin-bottom: 10px;
30+
}
31+
div {
32+
align-items: center;
33+
display: flex;
34+
}
35+
a {
36+
font-size: 14px;
37+
margin-right: 10px;
38+
text-decoration: none;
39+
padding-bottom: 0;
40+
border: 0;
41+
}
42+
span {
43+
font-size: 14px;
44+
margin-right: 5px;
45+
}
46+
ul {
47+
margin: 0;
48+
padding: 0;
49+
}
50+
button:before {
51+
align-self: center;
52+
border-style: solid;
53+
border-width: 6px 4px 0 4px;
54+
border-color: #ffffff transparent transparent transparent;
55+
content: "";
56+
height: 0;
57+
margin-right: 5px;
58+
width: 0;
59+
}
60+
`}</style>
61+
</section>
62+
)
63+
}
64+
return <div>Loading</div>
65+
}
66+
67+
const allPosts = gql`
68+
query allPosts($first: Int!, $skip: Int!) {
69+
allPosts(orderBy: createdAt_DESC, first: $first, skip: $skip) {
70+
id
71+
title
72+
votes
73+
url
74+
createdAt
75+
},
76+
_allPostsMeta {
77+
count
78+
}
79+
}
80+
`
81+
82+
// The `graphql` wrapper executes a GraphQL query and makes the results
83+
// available on the `data` prop of the wrapped component (PostList)
84+
export default graphql(allPosts, {
85+
options: {
86+
variables: {
87+
skip: 0,
88+
first: POSTS_PER_PAGE
89+
}
90+
},
91+
props: ({ data }) => ({
92+
data,
93+
loadMorePosts: () => {
94+
return data.fetchMore({
95+
variables: {
96+
skip: data.allPosts.length
97+
},
98+
updateQuery: (previousResult, { fetchMoreResult }) => {
99+
if (!fetchMoreResult) {
100+
return previousResult
101+
}
102+
return Object.assign({}, previousResult, {
103+
// Append the new posts results to the old one
104+
allPosts: [...previousResult.allPosts, ...fetchMoreResult.allPosts]
105+
})
106+
}
107+
})
108+
}
109+
})
110+
})(PostList)
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import React from 'react'
2+
import { gql, graphql } from 'react-apollo'
3+
4+
function PostUpvoter ({ upvote, votes, id }) {
5+
return (
6+
<button onClick={() => upvote(id, votes + 1)}>
7+
{votes}
8+
<style jsx>{`
9+
button {
10+
background-color: transparent;
11+
border: 1px solid #e4e4e4;
12+
color: #000;
13+
}
14+
button:active {
15+
background-color: transparent;
16+
}
17+
button:before {
18+
align-self: center;
19+
border-color: transparent transparent #000000 transparent;
20+
border-style: solid;
21+
border-width: 0 4px 6px 4px;
22+
content: "";
23+
height: 0;
24+
margin-right: 5px;
25+
width: 0;
26+
}
27+
`}</style>
28+
</button>
29+
)
30+
}
31+
32+
const upvotePost = gql`
33+
mutation updatePost($id: ID!, $votes: Int) {
34+
updatePost(id: $id, votes: $votes) {
35+
id
36+
votes
37+
}
38+
}
39+
`
40+
41+
export default graphql(upvotePost, {
42+
props: ({ ownProps, mutate }) => ({
43+
upvote: (id, votes) => mutate({
44+
variables: { id, votes },
45+
optimisticResponse: {
46+
updatePost: {
47+
id: ownProps.id,
48+
votes: ownProps.votes + 1
49+
}
50+
}
51+
})
52+
})
53+
})(PostUpvoter)
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { gql, graphql } from 'react-apollo'
2+
3+
function Submit ({ createPost }) {
4+
function handleSubmit (e) {
5+
e.preventDefault()
6+
7+
let title = e.target.elements.title.value
8+
let url = e.target.elements.url.value
9+
10+
if (title === '' || url === '') {
11+
window.alert('Both fields are required.')
12+
return false
13+
}
14+
15+
// prepend http if missing from url
16+
if (!url.match(/^[a-zA-Z]+:\/\//)) {
17+
url = `http://${url}`
18+
}
19+
20+
createPost(title, url)
21+
22+
// reset form
23+
e.target.elements.title.value = ''
24+
e.target.elements.url.value = ''
25+
}
26+
27+
return (
28+
<form onSubmit={handleSubmit}>
29+
<h1>Submit</h1>
30+
<input placeholder='title' name='title' />
31+
<input placeholder='url' name='url' />
32+
<button type='submit'>Submit</button>
33+
<style jsx>{`
34+
form {
35+
border-bottom: 1px solid #ececec;
36+
padding-bottom: 20px;
37+
margin-bottom: 20px;
38+
}
39+
h1 {
40+
font-size: 20px;
41+
}
42+
input {
43+
display: block;
44+
margin-bottom: 10px;
45+
}
46+
`}</style>
47+
</form>
48+
)
49+
}
50+
51+
const createPost = gql`
52+
mutation createPost($title: String!, $url: String!) {
53+
createPost(title: $title, url: $url) {
54+
id
55+
title
56+
votes
57+
url
58+
createdAt
59+
}
60+
}
61+
`
62+
63+
export default graphql(createPost, {
64+
props: ({ mutate }) => ({
65+
createPost: (title, url) => mutate({
66+
variables: { title, url },
67+
updateQueries: {
68+
allPosts: (previousResult, { mutationResult }) => {
69+
const newPost = mutationResult.data.createPost
70+
return Object.assign({}, previousResult, {
71+
// Append the new post
72+
allPosts: [newPost, ...previousResult.allPosts]
73+
})
74+
}
75+
}
76+
})
77+
})
78+
})(Submit)
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { ApolloClient, createNetworkInterface } from 'react-apollo'
2+
3+
let apolloClient = null
4+
5+
function _initClient (headers, initialState) {
6+
return new ApolloClient({
7+
initialState,
8+
ssrMode: !process.browser,
9+
dataIdFromObject: result => result.id || null,
10+
networkInterface: createNetworkInterface({
11+
uri: 'https://api.graph.cool/simple/v1/cixmkt2ul01q00122mksg82pn',
12+
opts: {
13+
credentials: 'same-origin'
14+
// Pass headers here if your graphql server requires them
15+
}
16+
})
17+
})
18+
}
19+
20+
export const initClient = (headers, initialState = {}) => {
21+
if (!process.browser) {
22+
return _initClient(headers, initialState)
23+
}
24+
if (!apolloClient) {
25+
apolloClient = _initClient(headers, initialState)
26+
}
27+
return apolloClient
28+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy