React: Context in Action

safar-safarov-MSN8TFhJ0is-unsplash.jpg

[Already know a bit about the basics of React? Jump straight to the section about context in React.]

Some Background on React

React is a powerful JavaScript library for building modern websites. The basic building blocks of React applications are React components, which allow developers to package up modules of code into reusable blocks of functionality. Components can then be deployed and used however often they're needed, with React itself managing the lifecycle of each component instance.

Components are useful, but your typical modern web application needs more than blocks of code rendering static content to serve its audience. It also needs the ability to manage data from different sources, whether they be images, inventory-related information, constants and input for calculating results, and the like. Additionally, a modern web application frequently needs the ability to reason over or execute logic against application data in order to respond to user interaction with the site.

It is for good reason that React also includes concepts (and rules) around how to pass data around in an application. In the case of React, data in an application is typically passed around in a specific way: parent component to direct child component, via props. Props differ from state, which React provides in order to keep track of stateful information that needs to be maintained for individual component instances, to assist in rendering those components. And used together with features such as React Hooks, applications can follow fairly complex patterns, where changes to a webpage's content can trigger other responses to that change in the application, all the while the information contained in the component's props and state can be referenced and used to enhance the application's ability to react and smartly render changes to the end user.

Props can be thought of as somewhat analogous to attributes on HTML components; they're slots where additional information about or associated with a component can be placed and made available to direct child components. For example: if a child component has to decide how to render based on the value of its parent component's CSS class, that child component can reference the parent component's className prop, retrieve its value, and include additional conditional logic to decide what it should render.

These features unlock a wealth of possibilities. But what do you do when the data you need to pass from a parent component needs to reach not a direct child, but a more distant descendant? This is where the concept of context in React comes into play.

Context in React

Context in React allows you to make certain information available to all descendant components. The benefit of this is that massive chains of prop handoffs from parent to child component can be avoided, and instead streamlined using context.

The snippets below show an example of context in action. The scenario: a simple React application that displays a static message, which can be overwritten with a different message when any one of the three buttons on the page is clicked. Besides this basic functionality, there is a very simple Google Analytics (GA) implementation, which requires client-side information about the user's browser: their browser vendor, and browser language. **Note that the example that follows is intended for client-side use/rendering.

Code Block 1. The application

[Click here for the CodePen link.]

function AppBody (props) {
  return (
    <div id='appRoot'>
      <ImportantMessage />
    </div>
  )
}

function ImportantMessage (props) {
  const [currentMessage, setCurrentMessage] = React.useState('Click a button to change this message.');
  const message1 = 'Hello.';
  const message2 = 'Hello, world.';
  const message3 = 'Hello, universe.';

  return (
      <div id='messageRoot'>
        <div id='messageText'>
          <h1>{currentMessage}</h1>
        </div>
        <div id='messageChangeButtons'>
          <button id='message1' onClick={() => { setCurrentMessage(message1); }}>Message 1</button>
          <button id='message2' onClick={() => { setCurrentMessage(message2); }}>Message 2</button>
          <button id='message3' onClick={() => { setCurrentMessage(message3); }}>Message 3</button>
        </div>
      </div>  
  );
}

// Render application
ReactDOM.render(
  <AppBody />,
  document.getElementById('rootDiv')
);

Running Code Block 1 (via local machine or using an online service such as CodePen), you should have a very basic React application with a message displayed in the viewport, which can be changed by clicking on any of the buttons.

Screenshot of the basic React application.

Screenshot of the basic React application.

By editing the first code block to match Code Block 2, you should not only see a pageview beacon, but beacons firing on button click, as well. These clicks will include basic information about which button was clicked. For this example, we'll require that the event fired on button click also include information in the beacon (via custom dimensions, the event label, and event action) about: the browser vendor, browser language, and the message associated with the event target button. And instead of re-interrogating the browser each time a button is clicked in order to determine the browser vendor and language, we will use context in React to determine the browser vendor and language when the top-most parent component is rendered, and make that information available to all descendants, including each button.

Code Block 2. Update: Using context to pass along browser vendor and language information

[Click here for the CodePen link.]

const clientInfo = {
  lang: window.navigator.language,
  vendor: window.navigator.vendor
}
const BrowserContext = React.createContext(clientInfo);

function AppBody (props) {
  return (
    <BrowserContext.Provider value={clientInfo}>
      <div id='appRoot'>
        <ImportantMessage />
      </div>
    </BrowserContext.Provider>
  );
}

function ImportantMessage (props) {
  const [currentMessage, setCurrentMessage] = React.useState('Click a button to change this message.');
  const message1 = 'Hello.';
  const message2 = 'Hello, world.';
  const message3 = 'Hello, universe.';

  // Using the useContext React Hook to consume context value
  const { lang, vendor } = React.useContext(BrowserContext);

  return (
    <div id='messageRoot'>
      <div id='messageText'>
        <h1>{currentMessage}</h1>
      </div>
      <div id='messageChangeButtons'>
        <button id='message1' onClick={() => {
          setCurrentMessage(message1);
          window.dataLayer.push({
            'event': 'click_message1',
            'message': message1,
            'lang': lang,
            'vendor': vendor
          });
        }}>Message 1</button>
        <button id='message2' onClick={() => {
          setCurrentMessage(message2);
          window.dataLayer.push({
            'event': 'click_message2',
            'message': message2,
            'lang': lang,
            'vendor': vendor
          });
        }}>Message 2</button>
        <button id='message3' onClick={() => {
          setCurrentMessage(message3);
          window.dataLayer.push({
            'event': 'click_message3',
            'message': message3,
            'lang': lang,
            'vendor': vendor
          });
        }}>Message 3</button>
      </div>
    </div>
  );
}

// Render application
ReactDOM.render(
  <AppBody />,
  document.getElementById('rootDiv')
);

With this context created, we're now able to easily look up the user's browser vendor and language information and include that in the GA beacons fired when any of the three message buttons are clicked.

With this sample, we now have a simple React application with a couple of Google Analytics beacons implemented, including beacons fired on click of any of the application buttons. These click event beacons include information provided by the context created in the React application for making browser vendor and language information available to all descendants of the <AppBody> component. This context made it far easier for more deeply nested components to access more globally available information.

As you can see, an analytics implementation within a React application can be complex. We have a team of experienced developers who can assist with your React-related business needs. Get in touch at info@campfireanalytics.com!

Thanks for reading.