Set Up Feature Flags with React Hooks

Everywhere I look lately, I see React Hooks. It’s the new trend that’s taken over the developer world. Hooks are functions that let you hook into state and lifecycle features from functional components. The value here is that they let you use React without classes. This makes for shorter, cleaner code.

Use React Hooks to Simplify Your Code

In this quick tutorial, you’ll be using the Split React SDK, the React Hooks API, and this Split repo to create a basic to-do list app with React, and optimizing it with Hooks. Your goal here is to create a to-do list app that allows for controlled rollout with feature flags.

In a typical React application, data is passed top-down (parent to child) via props, but this can become unorganized and overwhelming for certain types of props that are required by many components. Context allows components to access shared functionality without having to explicitly and manually pass props through every level of the component hierarchy.

In my previous blog post, I implemented a to-do list app in the “classic” React style, with a higher-order component — <SplitTreatments>, provided by the Split SDK -and props being passed down. In that tutorial I used a prop called allowDelete in my render function that was passed down from the parent component:

renderContent(deleteTreatment) {
  const allowDelete = deleteTreatment.treatment === "on";
  return (
    <div className="todoListMain">
      <div className="header">
        <form onSubmit={this.addItem}>
            ref={(a) => (this._inputElement = a)}
            placeholder="Enter Task"
          <button type="submit">Add</button>

However, it’s better to avoid passing props down from parent components, if you can avoid it. Imagine a large, complex application with a deep component hierarchy — you’d find yourself having to pass props down through many layers of intermediary components that don’t care about the props themselves (a problem known as prop drilling). That’s where the idea of Context comes into play.

Context lets you create a wrapper around your whole application that contains the shared functionality that your lower-level components want access to — your shared context. Any component that cares about this context can access it, via a useContext hook. Think of this as a shortcut that lets the component reach back up to get what it needs when it needs it. It’s a manageable way for things at the bottom of the hierarchy to know about the thing at the top.

You’ll use this Context facility to provide shared access to your feature flagging implementation. In the previous post, I wrapped my top-level TodoList component using withSplitFactory. This set up a SplitContext context provider at the top of our component hierarchy, which was then available to any nested component that needed to know about feature flags.

import { SplitTreatments, withSplitFactory } from "@splitsoftware/splitio-react"
export default withSplitFactory(sdkConfig)(TodoList);

You’ll use the same setup in the hook-based implementation of this app, but instead of having to use an extra higher-order component every time you want to access this Context, you instead just use the useContext hook. This is the functional way of making the connection back to the top of the application where you are managing Context:

import { useContext } from "react";
import { SplitContext } from "@splitsoftware/splitio-react";

export default function TodoItems({ deleteItem, entries }) {
  const { client } = useContext(SplitContext);
  const deleteTreatment = client.getTreatment(DELETE_TREATMENT);
  const allowDelete = deleteTreatment === "on";
  // ...

If you want to clean this up, you can extract some of that into a function in a separate file:

// useTreatment.js

import { useContext, useDebugValue } from "react";
import { SplitContext } from "@splitsoftware/splitio-react";

export default function useTreatment(treatmentName) {
  const { client } = useContext(SplitContext);

  const treatmentValue = client.getTreatment(treatmentName);
  useDebugValue(`${treatmentName}: ${treatmentValue}`);

  return treatmentValue;
// ToDoItems.js

export default function TodoItems({ deleteItem, entries }) {
  const allowDelete = useTreatment(DELETE_TREATMENT) === "on";

In this code, you need to go back up and reach for the shared context called SplitContext (provided by the React Split SDK). When you call withSplitFactory, it’s wrapping everything with that context provider. That’s at the top of the hierarchy. In your lower-level component, you want to use that thing, so you call useContext. You want access to SplitContext, specifically, which will give you access to the Split SDK. This will give you all the feature flag information your lower-level component needs regarding the state of the treatment. If it’s on, you allow delete, and if off, you do not allow delete.

This line, const allowDelete = useTreatment(DELETE_TREATMENT) === 'on';, takes care of all the feature flag plumbing. All you need to do is call the function. Whenever you render the todoitems component, you will use the useTreatment hook to pull the feature flag information inside the component and then allow the user to delete if it’s equal to on. The parent component doesn’t have to pass anything down anymore. It uses the hook to magically know the state of the flag.

Learn More About React, Hooks, and Feature Flags

With this approach, you not only have less code but also cleaner code. It lets you utilize functions like useContext to get information you need when you need it, and avoiding prop drilling (having to pass props down through each layer).

Using hooks rather than the standard class-based components is less boilerplate, easier to test, and you don’t need to always think of the internal state of the component.

Here’s a link to the entire repo. If you’re interested in learning more, check out these posts:

Be sure to follow us on Twitter @splitsoftware, and subscribe to our YouTube channel!

Want to Dive Deeper?

We have a lot to explore that can help you understand feature flags. Learn more about benefits, use cases, and real world applications that you can try.

Create Impact With Everything You Build

We’re excited to accompany you on your journey as you build faster, release safer, and launch impactful products.