How to Implement GraphQL in React

Implementing GraphQL in a React application allows developers to query precise data efficiently, reducing network overhead and improving performance. This guide provides a straightforward, step-by-step walkthrough of setting up Apollo Client, connecting it to a GraphQL API, and fetching data using React hooks like useQuery. By the end of this article, you will understand how to integrate GraphQL seamlessly into your React projects.

Step 1: Install Dependencies

To get started, you need to install Apollo Client, which is the standard library for managing GraphQL data in React, along with the core graphql package. Run the following command in your terminal:

npm install @apollo/client graphql

Step 2: Initialize Apollo Client

Next, configure the Apollo Client instance. This configuration defines the connection to your GraphQL API and sets up an in-memory cache to store queried data. Create a file named apolloClient.js and add the following code:

import { ApolloClient, InMemoryCache } from '@apollo/client';

const client = new ApolloClient({
  uri: 'https://your-graphql-endpoint.com/graphql', // Replace with your GraphQL API URL
  cache: new InMemoryCache(),
});

export default client;

Step 3: Connect Apollo Client to React

To make the Apollo Client accessible throughout your React application, wrap your root component with the ApolloProvider. This component uses React’s context API to provide the client to any nested component.

Update your index.js or main.jsx file:

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { ApolloProvider } from '@apollo/client';
import client from './apolloClient';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <ApolloProvider client={client}>
    <App />
  </ApolloProvider>
);

Step 4: Write and Execute a GraphQL Query

With the provider set up, you can now query data inside your React components. Use the gql parser to define your GraphQL query, and the useQuery hook to execute it and manage the request’s state (loading, error, and data).

Here is an example of a component fetching a list of users:

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

// Define the query
const GET_USERS = gql`
  query GetUsers {
    users {
      id
      name
      email
    }
  }
`;

function UserList() {
  const { loading, error, data } = useQuery(GET_USERS);

  if (loading) return <p>Loading users...</p>;
  if (error) return <p>Error: {error.message}</p>;

  return (
    <div>
      <h2>User List</h2>
      <ul>
        {data.users.map((user) => (
          <li key={user.id}>
            <strong>{user.name}</strong> - {user.email}
          </li>
        ))}
      </ul>
    </div>
  );
}

export default UserList;

Step 5: Mutate Data (Optional)

If you need to modify data on the server, you can use the useMutation hook. This works similarly to useQuery but returns a function that you can call to trigger the mutation.

import React, { useState } from 'react';
import { useMutation, gql } from '@apollo/client';

const ADD_USER = gql`
  mutation AddUser($name: String!, $email: String!) {
    addUser(name: $name, email: $email) {
      id
      name
    }
  }
`;

function AddUserForm() {
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');
  const [addUser, { loading, error }] = useMutation(ADD_USER);

  const handleSubmit = (e) => {
    e.preventDefault();
    addUser({ variables: { name, email } });
    setName('');
    setEmail('');
  };

  return (
    <form onSubmit={handleSubmit}>
      <input 
        value={name} 
        onChange={(e) => setName(e.target.value)} 
        placeholder="Name" 
        required 
      />
      <input 
        value={email} 
        onChange={(e) => setEmail(e.target.value)} 
        placeholder="Email" 
        required 
      />
      <button type="submit" disabled={loading}>
        {loading ? 'Adding...' : 'Add User'}
      </button>
      {error && <p>Error adding user: {error.message}</p>}
    </form>
  );
}

export default AddUserForm;