Skip to content

jabberbees/ab-react

Repository files navigation

ab-react

A simple React framework for A/B testing

NPM JavaScript Style Guide

Motivation

Provide a simple A/B testing framework to React developers with the following properties:

  • easy to setup with a Fluent API
  • deterministic: experiments' variants are chosen once, at application startup
  • the map of chosen variants can be queried at any time (for analytics or logging for example)
  • the map of chosen variants can be cached (LocalStorage for example)
  • an experiment variant can be represented by multiple React fragments
  • an experiment variant can be forced when the experiment is conclusive, without necessarily cleaning up other A/B variants
  • experiment variants can have different weights
  • the framework helps clean up past experiments (in LocalStorage for example)
  • tested (TDD)

Install

npm install --save @jabberbees/ab-react

How to configure experiments and variants

Configuration is done by:

  • creating an A/B configuration (singleton)
  • adding experiments and variants between configure and done function calls
  • providing this A/B configuration near the top of the application component tree to all child components
import { createAb, AbProvider } from '@jabberbees/ab-react';

const ab = createAb();

ab.configure()
  .addExperiment('hello-world')
  .addVariant("v1")
  .addVariant("v2").weighing(2)
  .addVariant("v3")
  .addVariant("v4")
  .addExperiment('zig-or-zag')
  .addVariant("zig")
  .addVariant("zag").forced()
  .done()

export default class App extends Component {
  render() {
    return (
      <AbProvider ab={ab}>
        ...
      </AbProvider>
    );
  }
}

How to initialise chosen variants

Chosen variants are determined by ab's "variant map".

The framework provides several functions to manipulate the variant map. All variant map functions can be chained in a Fluent way.

clearVariantMap() clears the variant map.

setVariantMap(variantMap) sets the variant map from an object containing experiment names as keys and variant names as value.

loadVariantMap(key, storage) loads the variant map from at the given key in storage. storage defaults to window.localStorage. Forced variants have priority over loaded variants.

saveVariantMap(key, storage) saves the variant map to the given key in storage.

removeSavedVariantMap(key, storage) removes the variant map stored at the given key in storage.

randomiseVariants() chooses a variant randomly for each experiment which is not already initialised in the variant map.

import { createAb } from '@jabberbees/ab-react';

const ab = createAb();

ab.loadVariantMap("ab-map", window.localStorage)
  .randomiseVariants()
  .saveVariantMap("ab-map", window.localStorage);

How to implement React variants

React variants are implemented by:

  • importing AbTestVariant component
  • wrapping variants JSX code inside an <AbTestVariant> component
import React, { Component } from 'react';
import { createAb, AbProvider, AbTestVariant } from '@jabberbees/ab-react';

const ab = createAb();

ab.configure()...

export default class App extends Component {
  render() {
    return (
      <AbProvider ab={ab}>
        <div>
          <AbTestVariant experiment='zig-or-zag' variant='zig'>
            <p>Zig</p>
          </AbTestVariant>
          <AbTestVariant experiment='zig-or-zag' variant='zag'>
            <p>Definitely Zag</p>
          </AbTestVariant>
        </div>
      </AbProvider>
    );
  }
}

Example

From example/src/App.js

// filename: App.js

import React, { Component } from 'react';
import { createAb, AbProvider, AbTestVariant } from '@jabberbees/ab-react';

const ab = createAb();

ab.configure()
  .addExperiment('hello-world')
  .addVariant("v1")
  .addVariant("v2").weighing(2)
  .addVariant("v3")
  .addVariant("v4")
  .addExperiment('zig-or-zag')
  .addVariant("zig")
  .addVariant("zag").forced()
  .done()
  .loadVariantMap("ab-map", window.localStorage)
  .randomiseVariants()
  .saveVariantMap("ab-map", window.localStorage);

export default class App extends Component {
  onResetVariants() {
    ab.removeSavedVariantMap("ab-map", window.localStorage);
    window.location.reload();
  }

  render() {
    return (
      <AbProvider ab={ab}>
        <div>
          <AbTestVariant experiment='hello-world' variant='v1'>
            <p>Hello world! (v1)</p>
          </AbTestVariant>
          <AbTestVariant experiment='hello-world' variant='v2'>
            <p>Hello world! (v2)</p>
          </AbTestVariant>
          <AbTestVariant experiment='hello-world' variant='v3'>
            <p>Hello world! (v3)</p>
          </AbTestVariant>
          <AbTestVariant experiment='hello-world' variant='v4'>
            <p>Hello world! (v4)</p>
          </AbTestVariant>

          <AbTestVariant experiment='zig-or-zag' variant='zig'>
            <p>Zig</p>
          </AbTestVariant>
          <AbTestVariant experiment='zig-or-zag' variant='zag'>
            <p>Definitely Zag</p>
          </AbTestVariant>

          <button onClick={this.onResetVariants}>Reset variants</button>
        </div>
      </AbProvider>
    );
  }
}

How to run the provided example

  • Open a shell window
cd path/to/ab-react
npm start
  • Open another shell window
cd path/to/ab-react/example
npm start
  • The example website should open in your favorite browser

Inspirations

Scoping A/B Test Code in React (Katie Garcia)

License

MIT © jabberbees

About

A simple React framework for A/B testing

Resources

Stars

Watchers

Forks

Packages

No packages published