GraphQL教程五:在React中使用GraphQL


尽管Graphiql界面非常好用,但其毕竟只是一个调试工具,并不适合最终用户。因此,我们需要在一个应用中调用GraphQL进行查询。下面介绍如何在React中(通过Apollo)使用GraphQL,比如查询学生信息,添加新学生等。

GraphQL系列教程:

创建React应用并添加依赖Apollo

安装create-react-app:

bash
npm install create-react-app -g
npx create-react-app frontend
cd frontend
npm start

这样,就能访问3000端口看到React APP了。

安装依赖库:

bash
npm install apollo-boost react-apollo graphql

然后在App.js中使用Applo Client:

javascript
import ApolloClient from 'apollo-boost';
import {ApolloProvider} from 'react-apollo';
import StudentList from "./components/StudentList";

const apolloClient = new ApolloClient({
  uri: 'http://localhost:4000/graphql'
})

function App() {
  return (
    <ApolloProvider client={apolloClient}>
      <div className="App">
        <h1>Students</h1>
        <StudentList/>
      </div>
    </ApolloProvider>
  );
}

export default App;

定义一个专门用来保存各种查询语句的组件

添加gql/queries.js,并添加后面可能用到的查询,比如getStudentsQuery, getTutorsQuery:

javascript
import {gql} from 'apollo-boost';

const getStudentsQuery = gql`
{
    students {
        id
        name
        address
    }
}
`

const getTutorsQuery = gql`
{
    tutors {
        id
        name
    }
}
`

export {getStudentsQuery, getTutorsQuery};

通过Apollo获取学生列表并显示在StudentList组件中

javascript
import React from 'react';
import { graphql } from 'react-apollo';
import NewStudent from './NewStudent';
import { getStudentsQuery } from '../gql/queries';

export default graphql(getStudentsQuery)(function StudentList(props) {

    return (
        <div>
            {
                props.data.loading? <h2>Loading...</h2>:
                props.data.students.map((student) => {
                    return(
                        <li key={student.id}>{student.name}, {student.address}</li>
                    )
                })
            }
            <NewStudent/>
        </div>
    );
    
})

需要注意的是这行略有些“奇怪”的语句:

javascript
export default graphql(getStudentsQuery)(function StudentList(props) 

本来函数式组件的声明是这样的:

javascript
export default function StudentList(props) 

经过graphql(getStudentsQuery)调用后,会将获取的数据,比如data(包含students)注入到组件的props中。

如果出现CORS问题,需要回到server项目中,安装cors:

bash
npm install cors

然后在app.js中添加:

javascript
const cors = require('cors');

app.use(cors());

实现Student组件

添加一个新的组件:components/NewStudent.js

javascript
import React from 'react';
import { graphql } from 'react-apollo';
import { getTutorsQuery } from '../gql/queries';

export default graphql(getTutorsQuery)(function NewStudent(props) {
    const loadTutors = () => {
        if(props.data.loading) {
            return(<option>Loading tutors...</option>);
        }else{
            return props.data.tutors.map((tutor)=>{
                return(<option key={tutor.id} value={tutor.id}>{tutor.name}</option>)
            })
        }
    }

    return (
        <div>
            <h3>Add a student</h3>
            <form id="new-student">
                <div className="input_field">
                    <label>Name</label>
                    <input type="text"/>
                </div>

                <div className="input_field">
                    <label>Address</label>
                    <input type="text"/>
                </div>

                <div className="input_field">
                    <label>Tutor</label>
                    <select>
                        <option>Select tutor</option>
                        {loadTutors()}
                    </select>
                </div>

                <button>Add</button>
            </form>
        </div>
    );
    
})

这样就可以看到如下的表单,界面丑了些,但这不是这个教程的重点:

GraphQL教程
GraphQL教程

通过调用GraphQL的mutation实现添加学生信息

安装依赖库

首先安装需要的一个包:

bash
npm install lodash

在NewStudent.js中引入:

javascript
import {flowRight as compose} from 'lodash';

使用useRef hook获取表单字段:

javascript
import React, {useRef} from 'react';

在组件内声明:

javascript
const nameElement = useRef('');
const addressElement = useRef('');
const tutorElement = useRef('');

在表单中使用:

markup
<div className="input_field">
    <label>Name</label>
    <input type="text" ref={nameElement}/>
</div>

<div className="input_field">
    <label>Address</label>
    <input type="text" ref={addressElement}/>
</div>

<div className="input_field">
    <label>Tutor</label>
    <select ref={tutorElement}>
        <option>Select tutor</option>
        {loadTutors()}
    </select>
</div>

在handleSubmit中就可以通过如下方式访问了:

javascript
nameElement.current.value

声明graphql mutation

修改gql/queries.js:

javascript
const addStudent = gql`
mutation($name: String!, $address: String!, $tutorId: ID!){
   addStudent(name: $name, address: $address, tutorId: $tutorId) {
       name
       id
   }
}
`

export {getStudentsQuery, getTutorsQuery, addStudent};

在React组件中使用两个GraphQL

javascript
import { getStudentsQuery, getTutorsQuery, addStudent } from '../gql/queries';

export default compose(
    graphql(getTutorsQuery, {name: "getTutorsQuery"}),
    graphql(addStudent, {name: "addStudent"})
)(function NewStudent(props) {

在表单提交事件中使用mutation

javascript
const handleSubmit = (e) => {
    e.preventDefault();
    props.addStudent({
        variables: {
            name: nameElement.current.value,
            address: addressElement.current.value,
            tutorId: tutorElement.current.value
        },
        refetchQueries: [{query: getStudentsQuery}]
    });
}

需要注意refetchQueries这行很重要,一旦添加学生,就会触发重新获取GraphQL的事件,使得页面自动更新。

添加学生详细信息组件

添加相应的GraphQL查询

更新gql/queries.js:

javascript
const getStudentQuery = gql`
    query($id: ID){
        student(id: $id) {
            id
            name
            address
            tutor {
                name
                id
            }
        }
    }
`

添加StudentDetails.js

javascript
import React, {useRef} from 'react';
import { graphql } from 'react-apollo';
import {getStudentQuery} from '../gql/queries';

export default graphql(getStudentQuery, {
    options: (props) => {
        return {
            variables: {
                id: props.id
            }
        }
    }
})(function StudentDetails(props) {

    const showStudent = () => {
        const {student} = props.data;
        
        if(student) {
            return (
                <div>
                    <h2>{student.name}</h2>
                    <p>{student.address}</p>
                    <p>Tutor: {student.tutor.name}</p>
                    
                </div>
            )
        } else {
            return(
                <h2>No student choosed.</h2>
            )
        }
    }

    return (
        <div id="student-details">
            {showStudent()}
        </div>
    )
})

更新StudentList组件

使用useState Hook:

javascript
import React, {useState} from 'react';

const [currentStudentId, setCurrentStudentId] = useState(null);

对于每条列表项,添加单击的响应事件:

javascript
<li onClick={(e) => {setCurrentStudentId(student.id)}} key={student.id}>{student.name}, {student.address}</li>

同时在页面中添加对StudentDetails的引用。


文章作者: 逻思
版权声明: 本博客所有文章除特別声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 逻思 !