紧接上篇react+graphql起手和特性介绍(二),介绍完graphql与koa的服务搭建和graphql的一些常用特性,接下来我们介绍下在react中如何使用graphql
我们使用create-react-app创建react应用:
npm i -g create-react-app
mkdir react-graphql-app
create-react-app react-graphql-app
安装以下前端依赖
npm install react-apollo graphql-tag graphql apollo-client apollo-cache-inmemory apollo-link-http
各个依赖包的作用:
- apollo-link-http 请求配置和网络请求能力
- apollo-cache-inmemory 数据缓存
- apollo-client 请求流程控制,生成请求数据,错误控制,响应数据解析
- graphql-tag 查询类的schema数据解析,包含对应的 webpack-loader
- react-apollo 连接graphql与react
- graphql 提供graphql的核心执行能力
然后我们进行react和graphql的整合
// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { ApolloClient } from 'apollo-client';
import { createHttpLink } from 'apollo-link-http';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { ApolloProvider } from 'react-apollo';
import App from './App';
// 我们可以自定义fetch,对请求进行统一处理
const customFetch = (uri, options) => {
return fetch(uri, options);
};
const client = new ApolloClient({
// 连接到graphql服务器
link: createHttpLink({ uri: 'http://localhost:9191/graphql', fetch: customFetch }),
// 设置缓存
cache: new InMemoryCache(),
});
// ApolloProvider 为react提供graphql能力
const WrappedApp = (
<ApolloProvider client={client}>
<App />
</ApolloProvider>
);
ReactDOM.render(WrappedApp, document.getElementById('root'));
为了能解析.graphql文件,需要修改webpack配置,添加graphql-loader
// config/webpack.config.dev.js && config/webpack.config.prod.js
...
module.exports = {
...
module: {
strictExportPresence: true,
rules: [
...
{
// 解析 .graphql/.gql 后缀的loader
test: /\.(graphql|gql)$/,
exclude: /node_modules/,
loader: 'graphql-tag/loader',
},
...
]
}
...
}
我们以post为示例,讲一下在组件中要做什么操作
创建定义查询的schema文件
# src/post.graphql
# 导入 user.graphql
#import "./user.graphql"
# fragment 定义片段,可以用于多个地方
fragment PostInfo on Post {
id
title
content
userId
user {
...UserInfo
}
}
query getPost($id: ID!) {
post(id: $id) {
id
title
content
userId
user {
id
name
age
available
birthday
money
gender
}
}
}
query getPosts {
posts {
...PostInfo
}
}
mutation createPost($data: PostInput!) {
createPost(data: $data) {
...PostInfo
}
}
# src/user.graphql
fragment UserInfo on User {
id
name
age
available
birthday
tags
gender
role
}
// src/App.js
import React, { Component } from 'react';
import Post from './Post';
class App extends Component {
render() {
return (
<div>
<Post/>
</div>
);
}
}
export default App;
// src/Post.js
import React, { Component } from 'react';
import { Query, Mutation, ApolloConsumer } from "react-apollo";
// 导入查询定义,定义的查询名次对应导入对象的方法名称,如 getPost => postQuery.getPost
import postQuery from './post.graphql';
class Post extends Component {
state = {
post: {},
postId: '',
newPost: {
title: '',
content: '',
}
}
render() {
// 由ApolloConsumer传入我们需要的数据,包含:
// data 正常返回时的数据
// loading 正在请求时loading会为true
// error 发生错误时error将会有错误数据
// 这里进行了重命名,将创建post,获取posts列表进行了命名区分
const {
client,
getPostsData, getPostsDataLoading,
createPost, createPostData, createPostLoading,
getPostsDataError
} = this.props;
const { postId, post, newPost } = this.state;
return(
<div>
<div>
{
// loading状态时将显示...
getPostsDataLoading ?
<div>...</div>
:
(
getPostsDataError ?
// 有错误数据,将会显示错误
<div>{JSON.stringify(getPostsDataError)}</div>
:
// 正常则显示请求到的数据
<div>{JSON.stringify(getPostsData.posts)}</div>
)
}
</div >
<hr />
<div>
<input
type="text"
name="postId"
value={postId}
onChange={(e) => {
this.setState({ postId: e.target.value })
}}
/>
<button onClick={() => {
// client 也是props传过来的对象,可以让我们主动发起请求
client.query({
// 对应定义的 getPost 查询
query: postQuery.getPost,
// 设置请求参数
variables: {
id: postId
}
}).then(({ data: { post } }) => {
this.setState({
post
})
})
}}>
getPost
</button>
<div>{JSON.stringify(post)}</div>
</div>
<hr/>
<div>
<input
type="text"
value={newPost.title}
onChange={(e) => this.setState({
newPost: {
...newPost,
title: e.target.value,
}
})}
/>
<input
type="text"
value={newPost.content}
onChange={(e) => this.setState({
newPost: {
...newPost,
content: e.target.value,
}
})}
/>
<button onClick={() => createPost({
// createPost是ApolloConsumer传过来的包装好的请求方法,
// 这里只用设置请求参数,loading,data,error 将会通过props
// 传递进来
variables: {
data: newPost
}
})}>
createPost
</button>
{
createPostLoading ?
<div>...</div>
:
<div>
{JSON.stringify(createPostData && createPostData.createPost)}
</div>
}
</div>
</div >
)
}
}
class PostWrap extends Component {
render() {
return (
<ApolloConsumer>
{(client) => (
// 传入要使用的motation查询
<Mutation mutation={postQuery.createPost}>
{(
// 方法重命名
createPost,
{
// 状态数据重命名
data: createPostData,
loading: createPostLoading
}
) => (
// 当同时多个查询时,使用这种嵌套模式
<Query
query={postQuery.getPosts}
>
{({
// 状态数据重命名
data: getPostsData,
loading: getPostsLoading,
error: getPostsDataError
}) =>
// 将重命名的状态数据和查询方法传递到组件中
// Query指定的查询在组件加载后就会自动发起请求
<Post {...{
client,
getPostsData,
getPostsLoading,
getPostsDataError,
createPost,
createPostData,
createPostLoading
}}
/>}
</Query>
)}
</Mutation>
)}
</ApolloConsumer>
)
}
}
export default PostWrap;
通过这种方式我们可以在react中使用graphql了,这种方式极大方便了我们对请求数据api的管理,而且可以通过整合查询,减少页面的请求次数。
如果你对这系列文章有疑问或发现有错误的地方,欢迎在下方留言讨论。