Implementing feature flags in single page apps, using React and Redux Saga

Built on frameworks like React, Angular, and Ember, single page apps (SPA) are becoming the new norm for modern applications. They center around dynamically updating a single HTML page via AJAX and HTML5 to deliver a more fluid and faster user experience. This introduces some new complexity when it comes to controlling access to front-end features, specifically via feature flags.

Feature flags (toggles) are used to gate access to particular code snippets, allowing you to control a feature’s rollout, target specific users, and kill a feature in production. The challenge with feature flagging single page apps is handling the state transformations (the changes in a webpage’s DOM) in a way that maintains performance and a fluid user experience.

This article discusses how to feature flag in a React single page app to show best practices. More specifically, we demonstrate feature flagging using LaunchDarkly’s JavaScript SDK.

Feature Flagging in Single Page Apps

With the help of React Router, creating a single page application can be a very quick and easy process, allowing developers to minimize risk and test features without degrading the user experience. We use Redux Sagas to handle asynchronous actions, which is perfect for integrating our app with the LaunchDarkly JavaScript SDK.

Feature Flags

In our app, we use two feature flags, user-type and header-bar-color. The user-type feature flag controls the content that is displaying depending on the group of current logged in user, while header-bar-color returns a hex code to toggle the color of the navigation bar.

An implementation of header-bar-color might look something like this:

Integrating Feature Flags with Redux Sagas

We handle the asynchronous process of requesting a LaunchDarkly feature flag by using Redux Sagas. The workflow begins in the App component of our SPA, where we dispatch an action to initialize the LaunchDarkly client.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

/app/components/App.js

classAppextendsComponent{

componentDidMount(){

this.props.dispatch(ldInitRequest())

}

render(){

…

}

}

/app/actions/index.js

export functionldInitRequest(){

return{type:LD_INIT_REQUEST}

}

Then, we can use a saga as middleware to wait for an initialization request to be received, using redux-saga’s takeEvery module, which will call an effect, initLD.

1

2

3

4

/app/sagas/index.js

export function*watchInitLD(){

yield takeEvery(LD_INIT_REQUEST,initLD)

}

The effect will call getLD, a function which creates a promise, waiting for the LaunchDarkly client to be ready for flag requests. Since the user is anonymous, we can generate a random token to be used as their key.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

/app/sagas/index.js

export function*initLD(){

let user={key:Math.random().toString(36).substring(7),anonymous:true}

ld=ldClient.initialize(YOUR_ENVIRONMENT_KEY,user)

constflag=yield call(getLD,user)

yieldput({type:LD_INIT,ld:ld,flag:flag})

}

functiongetLD(){

varldPromise=Promise.promisify(ld.on)

returnldPromise('ready').then(function(){

returnld.allFlags()

})

}

Once the client is ready, the effect will send the action off to our reducer, to store the client and current feature flag state in props.

Now that the LD client is initialized, and stored in our props, we can define a process to update our flag whenever the current user changes (logging in or out, for example). We take advantage of the fact that we’re already using sagas to asynchronously handle user authentication. When logging in, the authorize effect is called to ensure the login data is correct. When authorization is complete, we’ll make a call to idLD. This will identify a new user in our LaunchDarkly client, and send the correct flag to the reducer for our newly logged in user. Logging out works in a similar fashion. (Note: We make use of some utility functions defined in auth, which return user information from our local storage.)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

/app/sagas/index.js

exportfunction*authorize({username,password,isRegistering}){

try{

...

}catch(error){

...

}finally{

// When done, we tell Redux we're not in the middle of a request any more and

So, what do we do with the flags stored in our props? Well, in our Home component, we’ll render a different page depending on the flag that was received. In our case, if a fallback flag is received, the “Anonymous” page will be shown no matter who is logged in.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

/app/components/Home.js

classHomeextendsComponent{

render(){

switch(this.props.data.flag){

case2:

return(<Admin/>)

case1:

return(<User/>)

case0:

default:

return(<Anonymous/>)

}

}

}

Meanwhile, every time a page is loaded, the Nav component takes in the headerColor prop and changes the background of our navbar to said color.

What’s next?There are many ways to feature flag within a single page app, depending on your case and the complexity of your features. You can also check out this repo by TrueCar, which provides another avenue for feature flagging in React Redux.

Want to try out this feature flagging in React for yourself? Check out the open source repo!

As a designer who can code, Justin can empathize with a developer's workflow and design intuitive interfaces to address extremely complex functionality. He has built dozens of user interfaces for high-traffic applications — winning the Best of California IT Design award in 2012. He frequently contributes feature flag management and design theory articles to DZone, Tech.co, and DesignerHub. He holds degrees from UC Davis and USC, and is finishing an MS in Information Design at Northwestern. When he's not making developer's lives easier, he enjoys tennis, computer games, and writing.