0.0.1 • Published 4 years ago

@udacity/ureact-experiments v0.0.1

Weekly downloads
-
License
ISC
Repository
github
Last release
4 years ago

ureact-experiments

React components to help run A/B tests and feature flags.

Currently built based on Optimizely.

Usage

<OptimizelyProvider>

The OptimizelyProvider component is a wrapper that will instantiate everything you need to run A/B tests or feature flags with Optimizely.

ParamterTypeDescription
userobject: { id: string; attributes?: { key: string: any } }(Required) User ID and attributes to be passed to Optimizely for bucketing, etc.
projectIdstring(Optional) The Optimizely Project ID to use for any nested experiments and feature flags. Will default to the default "Udacity" project in Optimizely if not provided.
import React from 'react';
import {
  OptimizelyProvider,
} from '@udacity/ureact-experiments'


class AppWrapper extends React.Component {
  render() {
    return (
      <OptimizelyProvider
        user={{
          id: 'user123',
          attributes: {
            'device': 'iPhone',
            'is_logged_in': true
          }
        }}
        projectId='project-123'
      >
        <App />
      </OptimizelyProvider>
    )
  }
}

<Experiment>

The Experiment component will determine what variant of the given experiment the current user should see.

ParamterTypeDescription
experimentKeystring(Required) The experiment key.
attributesobject(Optional) Attributes for the user. Will override attributes supplied to the OptimizelyProvider.
defaultComponentReactNode(Optional) A component to show if the variation is undefined (still loading) or null (user was not bucketed in experiment).

If your experiment logic and components are simple, you can use the child render function provided:

import React from 'react';
import { Experiment } from '@udacity/ureact-experiments'

class MyComponent extends React.Component {
  render() {
    return (
      <Experiment key='experiment_key'>
        {(variation) => (
          variation === 'purple_button'
          ? <PurpleButton />
          : <BoringWhiteButton />
        )}
      </Experiment>
    )
  }
}

However, if your logic or component code is more complex, you may want to use the Variation component along with it:

import React from 'react';
import { Experiment } from '@udacity/ureact-experiments'

class MyComponent extends React.Component {
  render() {
    return (
      <Experiment key='experiment_key'>
        <Variation key='control'>
          <BoringWhiteButton />
        </Variation>

        <Variation key='blue_button'>
          <BlueButton />
        </Variation>

        <Variation key='purple_button'>
          <PurpleButton />
        </Variation>
      </Experiment>
    )
  }
}

<FeatureFlag>

The FeatureFlag component will determine whether or not the given feature flag is enabled for the current user.

ParamterTypeDescription
keystring(Required) The key of the feature flag.
attributesobject(Optional) Attributes for the user. Will override attributes supplied to the OptimizelyProvider.

Children of the <FeatureFlag> will only be shown if the feature is enabled:

import React from 'react';
import { FeatureFlag } from '@udacity/ureact-experiments'

class MyComponent extends React.Component {
  render() {
    return (
      <FeatureFlag key='feature_name'>
        <p>My feature is enabled!</p>
      </FeatureFlag>
    )
  }
}

Alternatively, if you want to be able to show a component when the feature is not enabled, you can use the child render function:

import React from 'react';
import { FeatureFlag } from '@udacity/ureact-experiments'

class MyComponent extends React.Component {
  render() {
    return (
      <FeatureFlag key='feature_name'>
        {(enabled) => (
          enabled 
          ? <p>My feature is enabled!</p>
          : <p>Feature is not enabled.</p>
        )}
      </FeatureFlag>
    )
  }
}

Debugging

Why is my experiment variation always "null"?

If you are always getting a variation of null, you may want to check the following:

  • Make sure you are passing in the correct projectId to the OptimizelyProvider. If you are not using the default "Udacity" project in Optimizely, you must pass in the project ID.
  • Make sure your experiment is on for the environment you are using
  • Make sure your traffic allocation is not set to 0% for your experiment

Contributing

Development

# Compile the package
npm run build

# Preview a demo of the components
npm start

# Run tests
npm test

# Lint the code
npm run lint

Watching Changes

To continually see your updates on the demo site, you will need to run two commands at once.
This is because we need to compile the code under /demo (to get the components and render a real app) in addition to compiling the actual package (under /src).

# Run the build and watch files for changes
npm run build

# In another terminal, run the dev server
npm start

Releasing

If you are testing a new feature, you should only release a prerelease version.

Once a prerelease has been tested, you can release a real version from the master branch.

Run the release command and follow the prompts. This will update the version, tag it, and publish to NPM for you.

npm run release

Notes

You may be aware that there is in fact a React SDK built by Optimizely, which is very similar to this one. Here are some reasons we decided to go ahead and create our own version of it.

  • Experiments-API benefits: This library interacts directly with Udacity's experiments-api. It is deployed to a CloudFlare worker which makes it very performant, but it also does quite a few things for us that we didn't want to have to re-implement in every web app:
    • Initializes Optimizely with a datafile, and keeps that datafile up-to-date via webhooks
    • Allows for multiple project IDs to be used (not possible with the Optimizely react-sdk).
    • Fires Segment events for you (upon Experiment/Feature activation, etc)
  • Consistency: Any Udacity services that need to fetch experiment data will (or should) use the experiments-api. It is nice to have the front end applications use the same API for the sake of consistency and developer understanding.
  • Future considerations: Other than the <OptimizelyProvider> component, we have tried to keep the rest of the components provider-agnostic. If we ever choose to move off of Optimizely, it shouldn't be hard to add a new provider component and continue using this library.