npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

react-test-tube

v1.0.1

Published

Run A/B split tests in your React application. Compatible with Google Analytics, Optimize and other popular A/B testing tools.

Downloads

35

Readme

React Test Tube

React Test Tube is a lightweight tool to perform A/B and multi-variate split tests in your React code.

Installation

Install React Test Tube using NPM.

$ npm i --save react-test-tube

Usage

To perform a simple A/B split test of two components in your React application create an <Experiment /> containing two or more <Variant /> components. By default the winning <Variant /> is chosen at random. You can customise this by providing a reducer function.

<Experiment name="my_experiment">
    <Variant name="my_variant_a">
        <ComponentA />
    </Variant>
    <Variant name="my_variant_b">
        <ComponentB />
    </Variant>
</Experiment>

Experiment

<Experiment
    name="my_experiment"
    reducer={Random()}
    cache={Cache()}
    onParticipation={participationCallback}
>
    ...
</Experiment>

| Prop | Description | Default | Required | | ---- | ----------- | ------- | -------- | | name | The name of the experiment. | - | Yes | | reducer | An instance of a reducer function. | Random | No | | cache | An instance of a cache function. | Cache | No | | onParticipation | A function that is called when a variant is chosen. | - | No |

Variant

<Variant name="my_variant_a">
    ...
</Variant>

| Prop | Description | Default | Required | | ---- | ----------- | ------- | -------- | | name | The name of the variant. | - | Yes |

Reducers

A reducer is a function that takes an array of <Variant /> components as an argument and returns a single winning <Variant /> which is to be rendered. Several reducer functions are included out of the box or you can create your own reducer function by providing it to the reducer prop of an <Experiment />.

Note: The built-in reducer functions are in fact factory functions whose return value is the reducer function itself, allowing for configuration of the reducer. This is why, when specifying a built-in reducer, we must use reducer={Random()} and not reducer={Random}. See "Creating a custom Reducer" for more details.

Random

The Random reducer selects a winning <Variant /> at random. It is the default reducer and does not need specifying.

import { Experiment, Variant, Random } from 'react-test-tube';

const App = () => (
    <Experiment name="my_experiment" reducer={Random()}>
        <Variant name="variant_a">
            <h1>Variant A</h1>
        </Variant>
        <Variant name="variant_b">
            <h1>Variant B</h1>
        </Variant>
    </Experiment>
);

Modulo

The Modulo reducer selects a winning <Variant /> based on a number provided in the configuration, such as a user ID. For an <Experiment /> with three <Variant /> components, when providing the number 2 as the only argument to the reducer, the calculation in effect is 2%3. The result is shifted by -1 in order to show the most logical <Variant />. Therefore, 2%3 would return the second <Variant /> at index 1 rather than the third (2%3 = 2).

import {Experiment, Variant, Modulo } from 'react-test-tube';

const App = ({ userId }) => (
    <Experiment name="my_experiment" reducer={Modulo(userId)}>
        <Variant name="variant_a">
            <h1>Variant A</h1>
        </Variant>
        <Variant name="variant_b">
            <h1>Variant B</h1>
        </Variant>
    </Experiment>
);

| Argument | Description | Default | Required | | -------- | ----------- | ------- | -------- | | id | An integer to use to determine the modulos, such as a user ID. | - | Yes |

Query String

The QueryString reducer selects a winning <Variant /> based on paramters provided in the query string of the URL. By default the parameters are exp (to specify the experiment) and var (to specify the variant). The reducer can be customised via its configuration to look at different parameters in the query string. Additionally, a fallbackReducer can be provided where the required parameters are not present in the URL. By default this is not provided and will return the first <Variant />.

import { Experiment, Variant, QueryString, Random } from 'react-test-tube';

const App = () => (
    <Experiment name="my_experiment" reducer={QueryString('exp', 'var', Random())}>
        <Variant name="variant_a">
            <h1>Variant A</h1>
        </Variant>
        <Variant name="variant_b">
            <h1>Variant B</h1>
        </Variant>
    </Experiment>
);

| Argument | Description | Default | Required | | -------- | ----------- | ------- | -------- | | experiment | The query string parameter that provides the ID of the experiment | exp | No | | variant | The query string parameter that provides the ID of the winning variant | var | No | | fallbackReducer | A reducer function that will be called if the query string does not contain the required parameters. | - | No |

Google Optimize

The GoogleOptimize reducer provides an integration with Google Optimize experiments, providing the advantages of managing and analysing your experiments from the Google Optimize tool whilst creating your variants as React components.

Required: Google Optimize is required to be installed on the page. Follow the instructions to install Google Optimize on your website.

Credit: The GoogleOptimize reducer uses the @react-hook/google-optimize library. If you do not require the additional functionality provided by react-test-tube then I recommend that you check out this library.

import { Experiment, Variant, GoogleOptimize } from 'react-test-tube';
import MyLoadingComponent from 'MyLoadingComponent';

const App = () => (
    <Experiment
        name="my_experiment"
        reducer={GoogleOptimize(
            'GOOGLE_OPTIMIZE_EXPERIMENT_ID',
            <MyLoadingComponent />,
            6000
        )}
    >
        <Variant name="variant_a">
            <h1>Variant A</h1>
        </Variant>
        <Variant name="variant_b">
            <h1>Variant B</h1>
        </Variant>
    </Experiment>
);

| Argument | Description | Default | Required | | -------- | ----------- | ------- | -------- | | experimentId | The ID of the experiment as provided in the Google Optimize interface. | - | Yes | | loadingState | A component that will be rendered whilst the winning <Variant /> is chosen. | null | No | | timeout | The time in ms in which a winning <Variant /> must be chosen before defaulting to show the first. | 3000 | No |

Creating a custom Reducer

A reducer is a function that takes an array of <Variant /> components as an argument and returns a single winning <Variant /> which is to be rendered. You can therefore provide any function that follows that convention.

const AlwaysChooseFirstVariant = variants => variants[0];

<Experiment name="my_experiment" reducer={AlwaysChooseFirstVariant}>
    ...
</Experiment>

As a reducer is just a function, you can also use a factory function to provide additional configuration. This is a pattern that is used for all built-in reducer functions.

/* This is just an example. I recommend coding more defensively than this! */
const HardCodeWinningVariant = index => variants => variants[index - 1];

<Experiment name="my_experiment" reducer={HardCodeWinningVariant(2)}>
    ...
</Experiment>

Caching

When a winning <Variant /> is chosen it is assumed that you will want your user to see the same <Variant /> on subsequent sessions, and not receive a different <Variant /> if they reload the page or navigate away and return later. The built-in Cache function caches the winning <Variant /> in Local Storage and returns it from the cache on future visits, bypassing the reducer function.

The built-in Cache is provided by default. You do not need to specify it when creating an <Experiment />. You can provide a custom cache function by setting the cache property.

A cache function is similar to a reducer. It is provided with the name of the experiment (to help avoid naming collisions with other cached experiments), the array of <Variant /> components, and a reducer function to run in the case of a miss (this reducer function is passed on from the <Experiment />).

const SessionStorageCache = () => (experiment, variants, reducer) => {
    const cacheKey = `exp_${experiment}_variant`;
    const cachedVariantName = window.sessionStorage.getItem(cacheKey);
    ...
    return chosenVariant;
};

<Experiment name="my_experiment" cache={SessionStorageCache()}>
    ...
</Experiment>

| Argument | Description | Default | Required | | -------- | ----------- | ------- | -------- | | experiment | The name of the experiment. Helps to avoid naming collisions. | - | Yes | | variants | An array of <Variant /> components. | - | Yes | | reducer | A reducer function to call if there is a cache miss. | - | Yes |

Participation Callback

When a winning <Variant /> is chosen you can access the name of both the <Experiment /> and the winning <Variant /> via the onParticipation callback.

const myOnParticipationCallback = (experimentName, variantName) => {
    console.log(`I chose the ${variantName} variant for the ${experimentName} experiment.`);    
};

<Experiment name="my_experiment" onParticipation={myOnParticipationCallback}>
    ...
</Experiment>

Contributing

If you find a bug or would like to contribute, please raise an issue and create a pull request on GitHub. Thanks!