Skip to main content

Apollo Client React Hooks with Drupal GraphQL - useQuery in Client, part 2

In the previous article we successfully set up a GraphQL server in Drupal 8 with the 4.x-version of the module. Our goal this time is to fetch data from the API by using the recently introduced useQuery()-hook and display the players data we have in the CMS in our frontend app.

Apollo Client React Hooks with Drupal GraphQL

Published on 2019-09-07 at 18:45

Drupal decoupled React.js GraphQL Apollo

Create the app, add our dependencies and modify the entry point

Something important first. As this article does not handle authentication etc. make sure to loosen up Drupals’ permission system appropriately, so that anonymous users have the permission “Players: Execute arbitrary requests” - something you probably must reconsider in production.

Let’s create a new React.js app as usual. As the article title says, we need some additional packages in addition to what is provided by the boilerplate command:

npm install apollo-boost @apollo/react-hooks graphql react-apollo

With that out of the way let’s edt the src/App.js file in the app to look like this:

import React, { useState } from 'react';
import Overview from './components/Overview';
import Player from './components/Player';
import PlayerContext from './context/PlayerContext';
import ApolloClient from "apollo-boost";
import { ApolloProvider } from 'react-apollo';

const client = new ApolloClient({
  uri: 'https://apollo-react-hooks-drupal-graphql.lndo.site/graphql'
});

const App = () => {
  const [activePlayerId, setActivePlayerId] = useState(0);

  return (
    <ApolloProvider client={client}>
      <PlayerContext.Provider value={
        {
          activePlayerId: activePlayerId,
          setActivePlayerId: setActivePlayerId
        }
      }>
        {activePlayerId ? <Player /> : <Overview /> }
      </PlayerContext.Provider>
    </ApolloProvider>
  )
}

export default App;

A few things to note here: we initialize the ApolloClient and pass it the uri of our Drupal installation. I use Lando, so this is the URL I get from it. You can see also that I chose /graphql as the endpoint in my GraphQL server. Furthermore in the App-component I set activePlayerId as state and assign it 0 initially. The idea here is that if the value differs than 0, then a detailed view of a player would be displayed, otherwise we should get a list of all players.

Creating the components needed

The Overview-component is a pretty simple one:

import React from 'react';
import Players from './Players';
import CreatePlayer from './CreatePlayer';

const Overview = () => {
  return (
    <React.Fragment>
      <Players />
    </React.Fragment>
  );
}

export default Overview;

At the moment it simply renders the Players-component. The reason to not render the Players-component itself directly here, it that later the Overview will get an additional component, the CreatePlayer. There we’ll show how to implement the useMutation()-hook. Let’s see how this Players-component looks like, but first of all let’s put our GraphQL-queries in a separate file, so that they can be imported where needed. For that let’s create a file src/gql/common.js:

import gql from "graphql-tag";

export const PLAYER = gql`
query Player ($id: Int!) {
  player(id: $id) {
    first_name
    last_name
  }
}
`;

export const PLAYERS = gql`
{
  players {
    items {
      id
      first_name
      last_name
    }
  }
}
`;

We now have all the ingredients needed to create our Players-component:

import React, { useContext } from 'react';
import PlayerContext from '../context/PlayerContext';
import { useQuery } from '@apollo/react-hooks';
import { PLAYERS } from '../gql/common';

const Players = () => {
  const context = useContext(PlayerContext);
  const { data, loading, error } = useQuery(PLAYERS);
  
  if (error) {
    return error.message;
  }

  return (
    <React.Fragment>
      <h1>Players</h1>
      {
        loading
        ?
        <p>Loading...</p>
        :
        data.players.items.map((item) => (
          <button key={item.id} onClick={() => context.setActivePlayerId(item.id)}>
            {item.first_name} {item.last_name} ({item.id})
          </button>
        ))
      }
    </React.Fragment>
  );
}

export default Players;

OK, we’re getting there. useQuery() receives the GraphQL-query as a parameter and this is really all it needs in order to connect to the API endpoint and receive data. The rest is pretty straightforward React.js-code - we render a loading indicator during loading and after that a list of buttons for each record. Clicking on the button would make use of the Context API and set the active player id to the current one in question, so that the detailed component Player.js would render.

import React, { useContext } from 'react';
import PlayerContext from '../context/PlayerContext';
import { useQuery } from '@apollo/react-hooks';
import { PLAYER } from '../gql/common';

const Player = () => {
  const context = useContext(PlayerContext);

  const { data, loading, error } = useQuery(PLAYER, {variables: {id: context.activePlayerId}});

  if (error) {
    return error.message;
  }

  return (
    <React.Fragment>
      <h1>Player data</h1>
      {
        loading
        ?
        <p>Loading...</p>
        :
        <React.Fragment>
          <p>First name: {data.player.first_name}</p>
          <button onClick={() => context.setActivePlayerId(0)}>Return to players list</button>
        </React.Fragment>
      }
    </React.Fragment>
  );
}

export default Player;

The thing to note in that component is that we provide useQuery with an additional parameter, an object that contains the id of the player node, which is passed into the query, in order to return the date for the one player in question.

Player data in Drupal and frontend
Player data in Drupal and frontend

That’s really it. useQuery() makes the component where it is used extremely easy understandable and more importantly also makes sure that the data requirements are in one place with the code responsible for rendering that data. This is one of the huge advantages of the new Apollo hooks. You can check out the code we wrote here over at this repository. In the next article we’ll take a look at another hook provided by Apollo - the useMutation().

Find me on LinkedIn or Twitter with any questions you have on this topic, looking forward to get in touch with you.

Share this