Colorfield logo

Drupalicious

Published on

React and Drupal 8 with JSON API 2/3

Authors
Drupal 8 and React logos

The first post of this serie focused on setting up easily a multilingual Drupal and React environment for a museum Audioguide web app.

This one describes the steps to achieve a MVP that displays a list and a detail page of Audio contents, so we have the opportunity to cover several basic concepts under React :

  • Components : add from a package repository, inherit, compose, create
  • Routes : default route and wildcard
  • Fetch to consume Rest
  • Localization via React Intl

French and English are being used to cover the translation feature in this demo.

EDIT

This second post used the React Starter Kit for React 15.x at the time of writing.
It should now be considered for concepts only. For an update of the code on React 16.x and the feature/react-intl branch of this boilerplate, refer to the part 3 and more specifically to the maintainer documentation.

Part 1 - The Drupal backend

Prepare the content

On the previous post, we prepared a basic Drupal environment to test the JSON API module with the default Article content type.
We now want a translatable Audio content type with some extra fields.

Option 1: create a new Drupal setup

If you just start from this post, here is a repo that provides what is needed for the Audioguide backend. It will avoid manual operations like the content type and language configuration and comes with some demo content.

git clone https://github.com/colorfield/audioguide-demo-drupal
# cd in the cloned repository and install dependencies
composer install

Then head to the Drupal installation and select the Configuration installer profile.

Drupal configuration installer

Let the default ../config/sync directory

Drupal default config sync

After completing installation, do not pay attention to the warning messages about missing languages inviting you to define a least another one, French is well installed. There is a patch being created to address an issue while shipping a profile with multiple languages and the drupal-2413191-21.patch was already applied during composer install.

Once installed, you can populate the default demo content via this URL /admin/content/demo.
Please note that the nodes translation are not related to each other and the MP3 files are not translated, but the demo content will provide 2 nodes in French and 2 in English, which is sufficient for our demo purpose.

Drupal audioguide demo content

Option 2 : continue from the previous setup

In the first part of the tutorial, we created 20 dummy Articles, first get rid of them.

# Assuming that you have devel generate enabled, see part 1
drush genc 0 --kill --types=article

Enable the Language and Content Translation module.

drush en language content_translation -y

Add the French language on /admin/config/regional/language

Then you can create the Audio content type and make it translatable.

Translatable content type

Define the following fields, and to keep it simple, all marked as required :

  • Title (default) - translatable by default when defining the content type as translatable.
  • Body (default) - translatable by checking the "Users may translate this field"
  • Id (field_id, integer) - not translatable
  • MP3 (field_mp3, file that allows mp3) - not translatable in this demo, but this will not affect the React code.
Audioguide field settings

Then devel generate content for both languages on /admin/config/development/generate/content and provide mp3 files.

Test the JSON API output

If everything went ok, you should now have a different JSON output per language.

/jsonapi/node/audio?filter\[langcode\]\[value\]=fr
/jsonapi/node/audio?filter\[langcode\]\[value\]=en

Drupal.org JSON API documentation about filtering, sorting and paginating.

Part 2 - The React frontend

A repository is also available for the React web app. What is described below lives in the 01-mvp branch.

Language setup for l10n (localization)

The following changes will define French as a translation language, instead of the default Czech configuration that comes with the React Intl branch of the React Starter Kit.

Change the default locales

Edit /src/config.js and change the default language by Canadian French or anything else, just make sure that it matches your Drupal configuration.

// change
locales: ['en-US', 'cs-CZ']
// to
locales: ['en-US', 'fr-CA']

Edit /src/client.js and again, change the default

// change
import cs from 'react-intl/locale-data/cs'
// (...)
;[en, cs].forEach(addLocaleData)

// to
import fr from 'react-intl/locale-data/fr'
// (...)
;[en, fr].forEach(addLocaleData)

Change the LanguageSwitcher language name

Edit the LanguageSwitcher component in /src/components/LanguageSwitcher/LanguageSwitcher.js

// change
const localeDict = {
  'en-US': 'English',
  'cs-CZ': 'Česky',
}

// to
const localeDict = {
  'en-US': 'English',
  'fr-CA': 'Français',
}

Extract messages

Messages that are used in the UI (via components, ...) can be extracted for translation. You can think of it as a potx equivalent.

This command creates the json files in the /src/messages directory

yarn run extractMessages
# or just
yarn start

Executing yarn start will also display Hubert Reeves mother tongue in the Language Switcher, check it here
http://localhost:3001/?lang=fr-CA

You can provide translations for each message attribute in the /src/messages/fr-CA.json file that was generated.

More translation options are covered on the documentation from the React Starter Kit.

Create the components

On this iteration, we will create two new React components :

  • AudioContentList : A translated list of Audio contents coming from Drupal, with a static localized introduction provided by a Markdown file.
  • AudioContentPage : The detail page of an Audio content (with title, id, body, mp3)

On the next iteration (part 3 of 3 from this tutorial), we will add

  • A profile selector (e.g. general, youth, blind), so we can demonstrate taxonomy term filtering by adding a term reference from a new Audio Profile vocabulary, on the Audio content type.
  • Search on Audio id and title fields.
  • Display an image for each Audio on teaser and detail page, so we can also introduce a reusable component for each React "display mode" equivalent.
  • Continuous play of the audio file while getting back to the list (YouTube app behavior).
  • Post comments on Audio content.

Hold a global reference to the Drupal domain

Edit the /src/constants/index.js file and append this const, not really sure if this is the best place but it seems to do the job.

// Change by your Drupal dev environment
export const REST_HOST_NAME = 'http://audioguide.dev'

Now we can head to the /src/components directory for creating our components.

Create the AudioContentList component

Create the AudioContentList directory that will hold 3 files :

  • package.json
  • AudioContentList.js
  • AudioContentList.css

This component uses two components provided by the boilerplate :

  • Page via inheritance
  • Link via composition

Note that the component should not fetch data on componentDidMount because, once mounted, it will not translate the nodes properly on language change.

So instead of what is mentioned in the code below, we should fetch the data from the route (see the React routing section below).

Also, one downside of using the Route method instead of the componentDidMount is that the web service is called on each display. But for the sake of simplicity, we do not cover here another way of solving this (e.g. listen to language change and render again with the new data).

Create the AudioContentPage component

On this one, we make use of a component from the npm package repository to get a basic audio player wrapper of the html5 tag.

First add it.

yarn add react-audio-player
# note that this is similar to
npm install --save react-audio-player

Create the AudioContentPage directory, with the same file structure

  • package.json
  • AudioContentPage.js
  • AudioContentPage.css

React routing

Go to the /src/routes directory.

Create the audio content list route

It will be the default route : localhost:3001/

Create the audio_list directory that will hold these 3 files :

  • index.js : the routing file
  • audio_list.md : the default static content
  • audio_list.fr-CA.md : the French localized static content
React Audioguide list

Here we use fetch to get Drupal data. There are other solutions like Axios, but fetch tends to become well adopted.
One noticeable fact is that we need to define this structure that complies JSON API

{
  audioList: {
    data: []
  }
}

Also, make sure that you have at least one node published as the JSON API module automatically filters unpublished nodes.

Create the audio content page route

The only noticeable change is the :id wildcard in the route that will be populated from the AudioContentList, with the Drupal node UUID. We make two calls : one for fetching the node, the other for the mp3 file. To be improved on the next iteration.

React Audioguide detail

Reference the new routes

Edit /src/routes/index.js

Remove the unnecessary routes and add the new ones, so we end up with something like this.

children: [
  require('./audio_list').default,
  require('./audio_page').default,
  // Wildcard routes, e.g. { path: '*', ... } (must go last)
  require('./notFound').default,
]

Routing documentation from the React Starter Kit.

Finish by removing the references to all the unused components on the Header.js and Footer.js component definition.
Delete the <Navigation /> and <Link /> tags.

That's all React folks, see you on the next iteration!

Resources