8000 Added section on mutations and local state · JavaScriptExpert/apollo@df4fcda · GitHub
[go: up one dir, main page]

Skip to content

Commit df4fcda

Browse files
committed
Added section on mutations and local state
1 parent 39d9c85 commit df4fcda

File tree

3 files changed

+160
-7
lines changed

3 files changed

+160
-7
lines changed

docs/source/tutorial/client.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ import { ApolloProvider } from 'react-apollo';
108108

109109
ReactDOM.render(
110110
<ApolloProvider client={client}>
111-
<App />
111+
<Pages />
112112
</ApolloProvider>, document.getElementById('root'));
113113
```
114114

docs/source/tutorial/local-state.md

Lines changed: 66 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,75 @@
11
---
22
title: "8. Manage local state"
3-
description: Start here for the Apollo fullstack tutorial
3+
description: How to store and query local data in the Apollo cache
44
---
55

6+
In almost every app we build, we display a combination of remote data from our graph API and local data such as network status, form state, and more. What's awesome about Apollo Client is that it allows us to store local data inside the Apollo cache and query it alongside our remote data with GraphQL.
7+
8+
We recommend managing local state in the Apollo cache instead of bringing in another state management library like Redux. The advantage of managing state this way is that the Apollo cache becomes the single source of truth for all data in our app and we don't have to synchronize our remote data with an external store.
9+
10+
Managing local data with Apollo Client is very similar to how you've already managed remote data in this tutorial. You'll write a client schema and resolvers for your local data. You'll also learn to query it with GraphQL just by specifying the `@client` directive. Let's dive in!
11+
612
<h2 id="local-schema">Write a local schema</h2>
713

8-
<h2 id="local-mutation">Update local data</h2>
14+
Just like how a schema is the first step toward defining our data model on the server, writing a local schema is the first step we take on the client.
15+
16+
Navigate to `src/resolvers.js` and copy the following code to create your client schema:
17+
18+
_src/resolvers.js_
19+
20+
```js
21+
import gql from 'graphql-tag';
22+
23+
export const schema = gql`
24+
extend type Query {
25+
isLoggedIn: Boolean!
26+
cartItems: [Launch]!
27+
}
28+
29+
extend type Launch {
30+
isInCart: Boolean!
31+
}
32+
33+
extend type Mutation {
34+
addOrRemoveFromCart: [Launch]
35+
}
36+
`;
37+
```
38+
39+
To build a client schema, we **extend** the types of our server schema and wrap it with the `gql` function. Using the extend keyword allows us to combine both schemas inside developer tooling like Apollo VSCode and Apollo DevTools.
40+
41+
We can also add local fields to server data by extending types from our server. Here, we're adding the `isInCart` local field to the `Launch` type we receive back from our graph API.
42+
43+
<h2 id="store-initializers">Initialize the store</h2>
44+
45+
Now that we've created our client schema, let's learn how to initialize the store. Since queries execute as soon as the component mounts, it's important for us to warm the Apollo cache with some default state so those queries don't error out. We will need to create `storeInitializers` for both `isLoggedIn` and `cartItems` to prevent these two local queries from erroring out:
46+
47+
Jump to `src/index.js` and specify your `storeInitializers` on the `ApolloClient` constructor:
48+
49+
_src/index.js_
50+
51+
```js lines=9-12
52+
const client = new ApolloClient({
53+
cache,
54+
link: new HttpLink({
55+
uri: 'http://localhost:4000/graphql',
56+
headers: {
57+
authorization: localStorage.getItem('token'),
58+
},
59+
}),
60+
storeInitializers: {
61+
isLoggedIn: () => !!localStorage.getItem('token'),
62+
cartItems: () => [],
63+
},
64+
});
65+
```
66+
67+
These `storeInitializers` will be called as soon as `ApolloClient` is created. They will also run if the user resets the cache.
68+
69+
Now that we've added default state to the Apollo cache, let's learn how we will query local data from within our React components.
970

1071
<h2 id="local-query">Query local data</h2>
1172

12-
<h2 id="testing">Testing local mutations</h2>
73+
<h2 id="local-mutation">Update local data</h2>
74+
75+
<h2 id="cart">Build a cart</h2>

docs/source/tutorial/mutations.md

Lines changed: 93 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,102 @@
11
---
22
title: "7. Update data with mutations"
3-
description: Start here for the Apollo fullstack tutorial
3+
description: Learn how to update data with the Mutation component
44
---
55

6+
With Apollo Client, updating data from a graph API is as simple as calling a function. Additionally, the Apollo Client cache is smart enough to automatically update in most cases. In this section, we'll learn how to use the `Mutation` component from `react-apollo` to login a user.
7+
68
<h2 id="query-component">What is a Mutation component?</h2>
79

10+
The `Mutation` component is another important building block in an Apollo app. It's a React component that provides a function to execute a GraphQL mutation. Additionally, it tracks the loading, completion, and error state of that mutation.
11+
12+
Updating data with a `Mutation` component from `react-apollo` is very similar to fetching data with a `Query` component. The main difference is that the first argument to the `Mutation` render prop function is a **mutate function** that actually triggers the mutation when it is called. The second argument to the `Mutation` render prop function is a result object that contains loading and error state, as well as the return value from the mutation. Let's see an example:
13+
814
<h2 id="fetch-data">Update data with Mutation</h2>
915

10-
<h2 id="pagination">Update the Apollo cache</h2>
16+
The first step is defining our GraphQL mutation. To start, navigate to `src/pages/login.js` and copy the code below so we can start building out the login screen:
17+
18+
_src/pages/login.js_
19+
20+
```js
21+
import React from 'react';
22+
import { Mutation, ApolloConsumer } from 'react-apollo';
23+
import gql from 'graphql-tag';
24+
25+
import LoginForm from '../components/login-form';
26+
27+
const LOGIN_USER = gql`
28+
mutation login($email: String!) {
29+
login(email: $email)
30+
}
31+
`;
32+
```
33+
34+
Just like before, we're using the `gql` function to wrap our GraphQL mutation so it can be parsed into an AST. We're also importing some components that we'll use in the next steps. Now, let's bind this mutation to our component by passing it to the `mutation` prop:
35+
36+
_src/pages/login.js_
37+
38+
```js
39+
export default function Login() {
40+
return (
41+
<Mutation mutation={LOGIN_USER}>
42+
{(login, { data }) => <LoginForm login={login} />}
43+
</Mutation>
44+
);
45+
}
46+
```
47+
48+
Our `Mutation` component takes a render prop function as a child that exposes a mutate function (`login`) and the data object returned from the mutation. Finally, we pass our login function to the `LoginForm` component.
49+
50+
To create a better experience for our users, we want to persist the login between sessions. In order to do that, we need to save our login token to `localStorage`. Let's learn how we can use the `onCompleted` handler on `Mutation` to persist our login:
51+
52+
<h3 id="apolloconsumer">Expose Apollo Client with ApolloConsumer</h3>
53+
54+
One of the main functions of `react-apollo` is that it puts your `ApolloClient` instance on React's context. Sometimes, we need to access the `ApolloClient` instance to directly call a method that isn't exposed by the `react-apollo` helper components. The `ApolloConsumer` component can help us access the client.
55+
56+
`ApolloConsumer` takes a render prop function as a child that is called with the client instance. Let's wrap our `Mutation` component with `ApolloConsumer` to expose the client. Next, we want to pass an `onCompleted` callback to `Mutation` that will be called once the mutation is complete with its return value. This callback is where we will save the login token to `localStorage`.
57+
58+
In our `onCompleted` handler, we also call `client.writeData` to write local data to the Apollo cache indicating that the user is logged in. This is an example of a **direct write** that we'll explore further in the next section on local state management.
59+
60+
_src/pages/login.js_
61+
62+
```js lines=3,4,7-10,16
63+
export default function Login() {
64+
return (
65+
<ApolloConsumer>
66+
{client => (
67+
<Mutation
68+
mutation={LOGIN_USER}
69+
onCompleted={({ login }) => {
70+
localStorage.setItem('token', login);
71+
client.writeData({ data: { isLoggedIn: true } });
72+
}}
73+
>
74+
{(login, { data }) => <LoginForm login={login} />}
75+
</Mutation>
76+
)}
77+
</ApolloConsumer>
78+
);
79+
}
80+
```
81+
82+
<h3 id="authenticate">Attach authorization headers to the request</h3>
83+
84+
We're almost done completing our login feature! Before we do, we need to attach our token to the GraphQL request's headers so our server can authorize the user. To do this, navigate to `src/index.js` where we create our `ApolloClient` and add the code below to the constructor:
85+
86+
_src/index.js_
87+
88+
```js lines=5,6
89+
const client = new ApolloClient({
90+
cache,
91+
link: new HttpLink({
92+
uri: 'http://localhost:4000/graphql',
93+
headers: {
94+
authorization: localStorage.getItem('token'),
95+
},
96+
})
97+
});
98+
```
99+
100+
Specifying the `headers` option on `HttpLink` allows us to read the token from `localStorage` and attach it to the request's headers each time a GraphQL operation is made.
11101

12-
<h2 id="testing">Test Mutation components</h2>
102+
Now that we know the basics, it's time to dive deeper into mutations. In the next section, we'll practice creating more complex `Mutation` components to manage local state in our app. We'll also learn how to query local data and write a local schema.

0 commit comments

Comments
 (0)
0