Nightingale Posing Mischievously
Join us to discuss the challenges, solutions and best practices for in-house JavaScript code sharing. Tuesday, 12/17 at 10am PT/1pm ET.Sign up here »

@wework/we-experiment-react

1.4.1 • Public • Published

WeExperiment React

React helpers for we-experiment-js.

Installation

Install using NPM:

npm install --save @wework/we-experiment-react

Usage

The main use-case for this library is using a Redux store.

Redux Setup

This library requires that an experiments reducer be mounted at the top-level of your store, under the key experiments.

A helper is provided for creating a reducer for the experiments, which is setup like so:

import { combineReducers, createStore } from 'redux';
import { combineExperiments } from 'we-experiment-react';
import reducers from 'reducers';
 
/*
  Assuming reducers is an object in the form of:
  {
    foo: reducer1,
    bar: reducer2,
    ...
  }
 */
 
const withExperimentsReducer = combineExperiments(reducers);
 
/*
  Resulting withExperimentsReducer is now:
  {
    foo: reducer1,
    bar: reducer2,
    ...
    experiments: experimentsReducer,
  }
 */
 
const rootReducer = combineReducers(withExperimentsReducer);
 
const store = createStore(rootReducer);
 

Alternatively, you can import the reducer directly and use it as you would normally:

import { combineReducers, createStore } from 'redux';
import { reducer as experimentsReducer } from 'we-experiment-react';
 
const rootReducer = {
  experiments: experimentsReducer,
  // your other reducers here
}
 
const store = createStore(rootReducer)
 

Fetch experiments

Experiments may be hydrated manually or fetched using a helper function which in turn uses we-experiment-js.

import { initExperiments, fetchExperiments } from 'we-experiment-react';
 
// Manual hydration:
const experiments = {
  foo: 'control',
  bar: 'variant_1',
};
 
store.dispatch(initExperiments(experiments));
 
// Fetch experiments:
const experimentNames = ['EXPERIMENT_1', 'EXPERIMENT_2'];
 
store.dispatch(fetchExperiments('USER_ID', 'SPLIT_API_KEY', experimentNames /*, options */));

Note that in order to use fetchExperiments, support for Promises is required for the Redux setup, for example, using redux-thunk.
Alternatively, it may be run like so:

fetchExperiments('USER_ID', 'SPLIT_API_KEY', experimentNames /*, options */)(store.dispatch);

See we-experiment-js's documentation for details on the available options.

Update experiments

It's possible to update the experiments fetched using fetchExperiments, using refreshExperiments. The refreshExperiments function should be called only after calling fetchExperiments. This function allows you to fetch new experiments or update existing ones, passing attributes that will be added to or override the ones passed on the call to fetchExperiments.

If any existing experiment names are passed, their value will be reset until the actual results are fetched.

import { fetchExperiments, refreshExperiments } from 'we-experiment-react';
 
// Fetch experiments:
const experimentNames = ['EXPERIMENT_1', 'EXPERIMENT_2'];
 
store.dispatch(fetchExperiments('USER_ID', 'SPLIT_API_KEY', experimentNames /*, options */));
 
// At this point, we have the results for EXPERIMENT_1, EXPERIMENT_2
 
// Update / fetch experiments:
const experimentsToUpdate = ['EXPERIMENT_2', 'EXPERIMENT_3'];
// Attributes will be added to or override the attributes passed to the initial `fetchExperiments`
// call, for this call only
const attributes = {};
store.dispatch(refreshExperiments(experimentsToUpdate, attributes));
 
// At this point, we have the results for EXPERIMENT_1, EXPERIMENT_2, EXPERIMENT_3.
// EXPERIMENT_2 has updated results, using the new attributes that were passed
 

Experiment Component

An Experiment component is provided for use-cases where a simple component replacement is required:

import { Experiment, Control, Variant } from 'we-experiment-react';
 
const experiment = (
  <Experiment name="EXPERIMENT_1">
    <Control>
      <Foo />
    </Control>
    <Variant name="variant_1">
      <Bar />
    </Variant>
  </Experiment>
);
 
ReactDOM.render(experiment, document.getElementById('container'));

Alternatively, Experiment may receive a render prop as its child:

<Experiment name="EXPERIMENT_1">
  {
    (variant) => variant === 'on' ? <Foo /> : <Bar />
  }
</Experiment>

Experiment Higher-Order Component

We provide 2 HOC's to retrieve experiments from store connectExperiment and connectExperiments

connectExperiment
import React from 'react';
import { connectExperiment } from 'we-experiment-react';
 
class Foo extends React.Component {
  render() {
    const variant = this.props.exp;
 
    if (!variant) {
      // Experiment is off.
      return null;
    }
 
    switch (variant) {
      case 'control':
        return <Bar />;
      case 'variant_1':
        return <Baz />;
    }
  }
}
Foo.propTypes = {
  exp: PropTypes.string,
};
 
export default connectExperiment('exp')(Foo);

By default, the variant will be passed as a prop with the same name as the experiment.

It is also possible to pass a propName option to change the prop's name:

export default connectExperiment('exp', { propName: 'woot' })(Foo);
connectExperiments
import React from 'react';
import { connectExperiments } from 'we-experiment-react';
 
class Foo extends React.Component {
  render() {
    const {
      exp1,
      exp2,
    } = this.props;
    
    if (exp1 === 'on' && exp2 === 'on') {
      return <div>Experiments are 'on'</div>;
    }
    
    return <div>Experiments are 'off'</div>;
  }
}
 
export default connectExperiments({
  exp1: true,
  exp2: true,
})(Foo);

By default, the variant will be passed as a prop with the same name as the experiment.

It is also possible to pass a prop name as value or propName option in object to change the prop's name:

 
export default connectExperiments({
  exp1:  'expX',
  exp2: {
    propName: 'expZ',
  },
})(Foo);

FeatureFlag Component

A FeatureFlag component is provided for simpler use-cases where only an on/off experiment exists.

This component will display its children only if the provided experiment's group equals to the activeValue prop's value (default: "on").

<FeatureFlag name="FEATURE_2">
  <Foo />
</FeatureFlag>

Feature HOC

In addition to the above component, a matching feature HOC is provided to allow same behavior at the component level.

export default feature('exp')(Foo);

This HOC also accepts an object as its optional second argument, with an activeValue key (default: "on") which behaves the same as the above component's prop of the same name.

Keywords

none

Install

npm i @wework/we-experiment-react

DownloadsWeekly Downloads

283

Version

1.4.1

License

UNLICENSED

Unpacked Size

44.7 kB

Total Files

23

Last publish

Collaborators

  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar
  • avatar