Written in the beginning

Because the technology stack used by the project team is GraphQL, and I used restful API before, I felt a little confused at the beginning. I believe there are other students in the same situation. After reviewing some materials and writing some demos, I think this part is a sort of knowledge for myself and a simple and straightforward guide for other students in the team who have not been in contact with GRAOHQL.

Ps: : one side dish chicken, please forgive and inform me if there is any mistake, 3Q

REST API is Dead, GraphQL lives on

Use react in

Most of graphQL front-end applications are based on existing frameworks, while our team is currently using Apollo, which has many sub-packages, and even react-Apollo which specifically supports React. However, the current team is choosing @Apollo /client, which is very simple and easy to use. Start with no more words.

Create a client

$ npx create-react-app my-app --typescript
Copy the code

To get started quickly, we built the project directly with create-React-app, but chose TS. Get a project with the structure shown when the build is complete.

Download Apollo and GraphQL dependencies

$ npm install @apollo/client graphql
Copy the code

In the download dependent project, we also made some changes to the project, and deleted unnecessary content in app.tsx. As well as the project does not need to delete the file, also can not delete, anyway does not affect us.

Index.tsx also removes unwanted code for a much cleaner look.

The steps to build Cilent are to provide a higher-order component of ApolloProvider with the component. ApolloProvider takes a client property. The code is as follows:

// src/index.tsx
import React from 'react'
import ReactDOM from 'react-dom'
import { ApolloClient, ApolloProvider, InMemoryCache } from '@apollo/client'
import './index.css'
import App from './App'

const client = new ApolloClient({
  uri: 'http://localhost:4000/graphql'.cache: new InMemoryCache(),
  //link:
})
ReactDOM.render(
  <React.StrictMode>
    <ApolloProvider client={client}>
      <App />
    </ApolloProvider>
  </React.StrictMode>.document.getElementById('root'))Copy the code

Here’s some explanation: New ApolloClient is an example of creating a client that takes an object as an argument and has three properties:

The property name describe
uri A server address, URI, or link to communicate with the Apollo client must exist, link being preferred
cache The Cache policy for Apollo clients recommends InMemoryCache. See the cache section on the official website for details
link Make the network layer, not too to understand, similar to the URI bar (later update up)

The client configuration is usually only configured once, and my back end here was built quickly using PrisMA-Nexus. However, this article mainly talks about the application of front-end GQL, so I won’t repeat it.

The request data

The easiest way to request data is to use the hook functions provided by Apollo, the most common of which is useQuery, which is very powerful and is the focus of this article, but has some limitations, which I’ll cover later, and the solution.

import React from 'react'
import { useQuery, gql } from '@apollo/client'

const GET_BLOGS = gql` query { blogs { name } } `
interface Blog {
  name: string
}
function App() {
  const { loading, error, data } = useQuery(GET_BLOGS)
  if (loading) return <div>loding</div>
  if (error) return <div>something error</div>
  return (
    <div className="App">
      {data.blogs.map((blog: Blog) => {
        return <div>{blog.name}</div>
      })}
    </div>)}export default App

Copy the code

NPM start starts the project and you can see that the page gets my data:

Back in the code, we used GQL to wrap our GraphQL query (see graphQL syntax here). Query blogs, then go to name. So we get an array of blog_name. The simplest request for data is complete, and there must be some questions at this point. g

Features and Disadvantages of useQuery (key points)

This is the focus of this article and what makes it different from other tutorials on the web. Most of the articles end there. But often do the front end of the students, may see the query results page may be confused, this seems to be the beginning of the component to automatically query ah, controllable? Well, controllable, but a few other properties of useQuery first.

Accept parameters

How the useQuery accepts arguments:

import React from 'react'
import { useQuery, gql } from '@apollo/client'

const GET_BLOGS = gql` query { blogs { name } } `

const GET_BlOG = gql` query($id: Int!) { blog(id: $id) { id name } } `
interface Blog {
  name: string
  id: number
}

function App() {
  const { loading, error, data } = useQuery(GET_BlOG, {
    variables: { id: 1}})if (loading) return <div>loding</div>
  if (error) return <div>something error</div>
  return <div className="App">{data.blog.name}</div>
}

export default App
Copy the code

As in the code above, I query the blog with id 1 and the result is as follows:

Poll and refresh

In fact, in the service provided by Apollo, the result returned by the server is cached. If the parameters are the same, the result returned by the server will be preferentially retrieved from the cache. However, the data on the server may have changed

Polling (Polling)

Polling provides a timed query that is enabled by passing a pollInterval parameter to the useQuery configuration. The default value is 0, that is, no polling

const { loading, error, data } = useQuery(GET_BlOG, {
    variables: { id: 1 },
    pollInterval: 1000,
  })
Copy the code

PollInterval 1000 pollInterval 1000 pollInterval 1000

You can see that the browser is constantly making requests. I don’t think there are many practical scenarios. Most of the parts of the business that need polling also need to be encapsulated and do some boundary conditions

The refresh (reFetch)

Refresh is a manual way to refresh your request data again, i.e. send the request again. As mentioned above, useQuery defaults to component builds that request once. ReFetch is enabled by fetching it from useQuery’s result:

import React from 'react' import { useQuery, gql } from '@apollo/client' const GET_BlOG = gql` query($id: Int!) { blog(id: $id) { id name } } ` interface Blog { name: string id: number } function App() { const { loading, error, data, refetch } = useQuery(GET_BlOG, { variables: { id: 1}, }) if (loading) return <div>loding</div> if (error) return <div>something error</div> return ( <div className="App"> {data.blog.name} <button onClick={() => refetch()}>click me torefetch</button> </div> ) } export default AppCopy the code

Start the project, open the browser, refresh the page, and you can see that the component builds with a GraphQL request, and then clicks the button again.

One problem is that loading will not be punished in the process of reFetch, that is, the page will keep the previous UI first and refresh directly after the result of reFetch comes back. If want to do a loading effect, here need to use another attribute: networkStatus, use usequery configuration in the same it notifyOnNetworkStatusChange:

import React from 'react'
import { useQuery, gql, NetworkStatus } from '@apollo/client'

const GET_BlOG = gql` query($id: Int!) { blog(id: $id) { id name } } `
interface Blog {
  name: string
  id: number
}

function App() {
  const { loading, error, data, refetch, networkStatus } = useQuery(GET_BlOG, {
    variables: { id: 1 },
    notifyOnNetworkStatusChange: true,})if (networkStatus === NetworkStatus.refetch) return <div>refetch loading</div>
  if (loading) return <div>loding</div>
  if (error) return <div>something error</div>
  return (
    <div className="App">
      {data.blog.name}
      <button onClick={()= > refetch()}>click me torefetch</button>
    </div>)}export default App

Copy the code

mutation

Emm To be honest, I forgot the word does not exist, it is not strictly written here, but since this article is mainly for team members, it is ok, and the phenomenon still exists. If the variable on which the GraphQL query depends changes, GraphQL will revisit it. This sounds a bit like Refetch, but we didn’t do it manually. However, it can be used properly to achieve similar results as refetch, as shown below

import React, { useState } from 'react'
import { useQuery, gql, NetworkStatus } from '@apollo/client'

const GET_BlOG = gql` query($id: Int!) { blog(id: $id) { id name } } `
interface Blog {
  name: string
  id: number
}

function App() {
  const [id, setId] = useState<number> (1)
  const { loading, error, data, refetch, networkStatus } = useQuery(GET_BlOG, {
    variables: { id },
    notifyOnNetworkStatusChange: true,})const upId = () = > {
    setId(2)}if (networkStatus === NetworkStatus.refetch) {
    alert(1)
    return <div>refetch loding</div>
  }
  if (loading) return <div>loding</div>
  if (error) return <div>something error</div>
  return (
    <div className="App">
      {data.blog.name}
      <div>
        <button onClick={upId}>click me upid</button>
      </div>
    </div>)}export default App

Copy the code

Open the browser, after I click the button and assign the ID value to 2, I re-send the request, the cut ID is changed to 2, and the query result is also changed:

In my opinion, this is a nice feature to use, and one that needs to be looked out for, as it can cause some unexpected changes if you don’t

UseQuery’s disadvantage is that it cannot be truly manual

With all these features, it seems that you can implement many scenarios, but as someone who is used to the restapi, how can you still feel so uncomfortable? Can’t seem to get the hang of it? Yes, both mutations and refreshes are not really manual requests, they are made at least once during component builds. I don’t need it. Usequery is not useful if we want to click a button to query, etc. So there’s another hook-uselazyQuery, which I find rarely mentioned in many articles.

UseLazyQuery – True manual query

Most of the properties and behavior of useLazyQuery are similar to that of useQuery, but it is important to note that useLazyQuery does not execute immediately after it is called (and therefore does not query without worrying about component building). Instead, it returns a function that you call the query:

import React, { useState } from 'react'
import { useQuery, gql, NetworkStatus, useLazyQuery } from '@apollo/client'
const GET_BlOG = gql` query($id: Int!) { blog(id: $id) { id name } } `
interface Blog {
  name: string
  id: number
}

function App() {
  const [id, setId] = useState<number> (1)
  const [getBlog, { loading, data }] = useLazyQuery(GET_BlOG, {
    variables: { id },
    notifyOnNetworkStatusChange: true,})const getData = () = > {
    getBlog()
  }

  if (loading) return <div>loding</div>
  return (
    <div className="App">
      {data && data.blog.name}
      <div>
        <button onClick={getData}>click me upid</button>
      </div>
    </div>)}export default App

Copy the code

If you open your browser, you’ll see that there’s no data in the initial page, and when you click on the button, you’ll send a request to get the data. Note that useQuery is the object that’s returned, whereas useLazyQuery is an array. The first item is a query function, and the second item is a collection of other values

The last

If there is any mutation, I will record it. In addition, I would like to say that both useQuery and useLazyQuery are hook forms. I do not know how to use them in the class component. It’s probably going to be a little different, but I’ve used the client’s Query method to implement the original query before, so even class is certainly still graphQL.