让我们使用AWS AppSync和React来创建一个ToDo应用程序(3)创建React应用程序
首先
通过前几篇文章,我们使用了AWS AppSync创建了一个GraphQL API。
这次,我们打算使用连接到GraphQL API的客户端React来完成。
AWS AppSync提供了与React相关的GraphQL客户端Apollo的支持,aws-appsync-react(绑定库)已经准备好了,所以这次我们将使用它来关联AWS AppSync的数据和组件。
项目的设置
创建原型
使用Create React App工具创建项目的雏形。
(本次使用的Node版本是9.2.0。)
$ mkdir aws-appsync-todo-app
$ npx create-react-app .
安装所需的附加软件包。
$ yarn add graphql-tag react-apollo aws-appsync aws-appsync-react uuid
-
- graphql-tag
GraphQLのスキーマをJavaScriptのコード内に定義するために使用
react-apollo
GraphQLクライアント
aws-*のパッケージ
AWS AppSyncとApolloを連携するために使用
uuid
Todo個々のアイテムにクライアント側でユニークなIDをつけるために使用
获取和导入设置文件
从控制台界面打开“AWS AppSync > 创建的项目 > 首页”,然后下载AppSync.js文件,方法是在“入门指南”的最下方点击“下载 AWS AppSync.js 配置文件”。

将下载的文件放置在创建的项目的src文件夹下,与其他包一起导入到App.js中。
import { ApolloProvider } from 'react-apollo';
import AWSAppSyncClient from "aws-appsync";
import { Rehydrated } from "aws-appsync-react";
import appSyncConfig from "./AppSync";
初始化AppSyncClient
使用从配置文件中读取的AWS认证信息和API信息作为参数来初始化AWS AppSyncClient。
可以通过auth参数选择认证方式,但本次使用API密钥进行认证。
// AWS AppSync Client
const client = new AWSAppSyncClient({
url: appSyncConfig.graphqlEndpoint,
region: appSyncConfig.region,
auth: {
type: appSyncConfig.authenticationType,
apiKey: appSyncConfig.apiKey,
}
});
将与AppSync数据进行集成的组件包括ApolloProvider和Rehydrated,在ApolloProvider中传递AppSyncClient。
class App extends Component {
render() {
return (
<ApolloProvider client={client}>
<Rehydrated>
<div className="App">
<header className="App-header">
<h1 className="App-title">AWS AppSync Todo</h1>
</header>
<TodoListWithData />
</div>
</Rehydrated>
</ApolloProvider>
);
}
}
创建GraphQL查询
使用graphql-tag的gql方法来定义查询。这次我们将其创建在src/GraphQL目录下。
查询所有完成的事项
import gql from "graphql-tag";
export default gql(`
query {
getTodos {
id
title
description
completed
}
}`);
创建一个名为Todo的变异
import gql from "graphql-tag";
export default gql(`
mutation addTodo($id: ID!, $title: String, $description: String, $completed: Boolean) {
addTodo(
id: $id
title: $title
description: $description
completed: $completed
) {
id
title
description
completed
}
}`);
Todo更新的Mutation
import gql from "graphql-tag";
export default gql(`
mutation updateTodo($id: ID!, $title: String, $description: String, $completed: Boolean) {
updateTodo(
id: $id
title: $title
description: $description
completed: $completed
) {
id
title
description
completed
}
}`);
删除Todo的变异
import gql from "graphql-tag";
export default gql(`
mutation deleteTodo($id: ID!) {
deleteTodo(id: $id) {
id
title
description
completed
}
}`);
实现ToDoList组件
这次,将Todo列表的全部功能简略地整合到一个组件中。
在src/Components文件夹下,新建了一个TodoList.js文件。
AWS AppSync和Component之间的协作
为了在React组件中与AWS AppSync的GraphQL API进行协作,我们将使用react-apollo。
当将组件作为参数传递给React-Apollo中的graphql方法时,可以接收到一个组件,该组件可以通过props从GraphQL API获取到的数据。
const TodoListWithData = graphql(QueryGetTodos)(TodoList);
此外,当需要将多个查询、变动和组件进行协作时,可以使用react-apollo的compose方法。实际上,这只是一个想象,因为在实际应用中可能需要为每个查询、变动和组件指定一些选项等。
const TodoListWithData = compose(
graphql(QueryGetTodos),
graphql(MutationAddTodo),
graphql(MutationUpdateTodo),
graphql(MutationDeleteTodo),
)(TodoList);
实际上,将每个查询(Query)和变更(Mutation)关联起来的部分如下所示。
export default compose(
// 全件取得Query
graphql(QueryGetTodos, { // あらかじめ定義したGraphQLクエリを使用
options: {
fetchPolicy: 'cache-and-network'
},
props: (props) => ({
todos: props.data.getTodos
})
}),
// 追加Mutation
graphql(AddTodoMutation, { // あらかじめ定義したGraphQLクエリを使用
props: (props) => ({
onAdd: (todo) => {
props.mutate({
variables: { ...todo },
// APIからのレスポンスが返ってくるまえにpropsに反映する値を設定
optimisticResponse: () => ({ addTodo: { ...todo, __typename: 'Todo' } })
})
}
}),
options: {
// 追加の後に全件リストを更新するアクション
refetchQueries: [{ query: QueryGetTodos }],
update: (proxy, { data: { addTodo } }) => {
const query = QueryGetTodos;
const data = proxy.readQuery({ query });
data.getTodos.push(addTodo);
proxy.writeQuery({ query, data });
}
}
}),
// 状態更新(チェック)Mutation
graphql(UpdateTodoMutation, { // あらかじめ定義したGraphQLクエリを使用
props: (props) => ({
onCheck: (todo) => {
props.mutate({
variables: { id: todo.id, title: todo.title, description: todo.description, completed: !todo.completed },
// APIからのレスポンスが返ってくるまえにpropsに反映する値を設定
optimisticResponse: () => ({ updateTodo: { id: todo.id, title: todo.title, description: todo.description, completed: !todo.completed, __typename: 'Todo' } })
})
}
}),
options: {
// 更新の後に全件リストを更新するアクション
refetchQueries: [{ query: QueryGetTodos }],
update: (proxy, { data: { updateTodo } }) => {
const query = QueryGetTodos;
const data = proxy.readQuery({ query });
data.getTodos = data.getTodos.map(todo => todo.id !== updateTodo.id ? todo : { ...updateTodo });
proxy.writeQuery({ query, data });
}
}
}),
// 削除Mutation
graphql(DeleteTodoMutation, { // あらかじめ定義したGraphQLクエリを使用
props: (props) => ({
onDelete: (todo) => props.mutate({
variables: { id: todo.id },
// APIからのレスポンスが返ってくるまえにpropsに反映する値を設定
optimisticResponse: () => ({ deleteTodo: { ...todo, __typename: 'Todo' } }),
})
}),
options: {
// 削除の後に全件リストを更新するアクション
refetchQueries: [{ query: QueryGetTodos }],
update: (proxy, { data: { deleteTodo: { id } } }) => {
const query = QueryGetTodos;
const data = proxy.readQuery({ query });
data.getTodos = data.getTodos.filter(todo => todo.id !== id);
proxy.writeQuery({ query, data });
}
}
})
)(TodoList);
其余部分如下:
class TodoList extends Component {
constructor(props) {
super(props);
this.state = {
todo: {
title: '',
description: ''
},
};
}
// propsの初期値を設定
static defaultProps = {
todos: [],
onAdd: () => null,
onDelete: () => null,
onUpdate: () => null,
}
todoForm = () => (
<div>
<span><input type="text" placeholder="タイトル" value={this.state.todo.title} onChange={this.handleChange.bind(this, 'title')} /></span>
<span><input type="text" placeholder="説明" value={this.state.todo.description} onChange={this.handleChange.bind(this, 'description')} /></span>
<button onClick={this.handleOnAdd}>追加</button>
</div>
);
renderTodo = (todo) => (
<li key={todo.id}>
<input type="checkbox"checked={todo.completed} onChange={this.handleCheck.bind(this, todo)} />
{!todo.completed && todo.title}
{todo.completed && (<s>{todo.title}</s>)}
<button onClick={this.handleOnDelete.bind(this, todo)}>削除</button>
</li>
);
handleChange = (field, { target: { value }}) => {
const { todo } = this.state;
todo[field] = value;
this.setState({ todo });
}
handleOnAdd = () => {
if (!this.state.todo.title || !this.state.todo.description) {
return;
}
const uuid = uuidv4();
const newTodo = {
id: uuid,
title: this.state.todo.title,
description: this.state.todo.description,
completed: false
}
this.props.onAdd(newTodo);
const { todo } = this.state;
todo.title = '';
todo.description = '';
this.setState({ todo });
}
handleCheck = (todo) => {
this.props.onCheck(todo);
}
handleOnDelete = (todo) => {
this.props.onDelete(todo);
}
render() {
const { todos } = this.props;
return (
<Fragment>
{this.todoForm()}
<ul>
{todos.map(this.renderTodo)}
</ul>
</Fragment>
);
}
}
请按照以下选项进行参考
用ReactJS构建客户端应用程序 -AWS AppSync
使用React和Apollo尝试Github GraphQL API -Qiita
APOLLO客户端 -Apollo
简要总结
关于客户端实现的部分,很多都依赖于react-apollo。如果要充分利用AWS AppSync和React的组合,我们需要仔细查看react-apollo的帮助方法和缓存选项等。
另一方面,除此之外的部分都由aws-appsync-react自动处理,不需要担心。
本次只使用了Query和Mutation来进行GraphQL查询,但如有机会,我也想尝试使用Subscription来与服务器实时通信的模式。