react+graphql起手和特性介紹(三)

緊接上篇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

安裝如下前端依賴node

npm install react-apollo graphql-tag graphql apollo-client apollo-cache-inmemory apollo-link-http

各個依賴包的做用:react

  • apollo-link-http 請求配置和網絡請求能力
  • apollo-cache-inmemory 數據緩存
  • apollo-client 請求流程控制,生成請求數據,錯誤控制,響應數據解析
  • graphql-tag 查詢類的schema數據解析,包含對應的 webpack-loader
  • react-apollo 鏈接graphql與react
  • graphql 提供graphql的核心執行能力

而後咱們進行react和graphql的整合webpack

// 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-loaderweb

// 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文件npm

# 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的管理,並且能夠經過整合查詢,減小頁面的請求次數。
若是你對這系列文章有疑問或發現有錯誤的地方,歡迎在下方留言討論。segmentfault

相關文章
相關標籤/搜索