Giter Club home page Giter Club logo

react-ab-test's Introduction

A/B Testing React Components

NPM version Circle CI Dependency Status NPM Downloads

Wrap components in <Variant /> and nest in <Experiment />. A variant is chosen randomly and saved to local storage.

<Experiment name="My Example">
  <Variant name="A">
    <div>Version A</div>
  </Variant>
  <Variant name="B">
    <div>Version B</div>
  </Variant>
</Experiment>

Report to your analytics provider using the emitter. Helpers are available for Mixpanel and Segment.com.

emitter.addPlayListener((experimentName, variantName) => {
  mixpanel.track('Start Experiment', {
    name: experimentName,
    variant: variantName,
  });
});

Please โ˜… on GitHub!

Table of Contents

Installation

react-ab-test is compatible with React >=0.14.x

yarn add @marvelapp/react-ab-test

Usage

Standalone Component

Try it on JSFiddle

Using useExperiment Hook

import React from 'react';
import { useExperiment, emitter } from '@marvelapp/react-ab-test';

// Hook usage pattern requires registration of experiments
emitter.defineVariants("My Example", ["A", "B"]);

const App = () => {
  const { selectVariant, emitWin } = useExperiment("My Example");
  const variant = selectVariant({
    A: <div>Section A</div>,
    B: <div>Section B</div>
  });

  return (
    <div>
      {variant}
      <button onClick={emitWin}>CTA</button>
    </div>
  );
};

Using Experiment Component

import React from 'react';
import { Experiment, Variant, emitter } from '@marvelapp/react-ab-test';

class App extends Component {
  experimentRef = React.createRef();

  onButtonClick(e) {
    this.experimentRef.current.win();
  }

  render() {
    return (
      <div>
        <Experiment ref={this.experimentRef} name="My Example">
          <Variant name="A">
            <div>Section A</div>
          </Variant>
          <Variant name="B">
            <div>Section B</div>
          </Variant>
        </Experiment>
        <button onClick={this.onButtonClick}>Emit a win</button>
      </div>
    );
  }
}

// Called when the experiment is displayed to the user.
emitter.addPlayListener(function(experimentName, variantName) {
  console.log(`Displaying experiment ${experimentName} variant ${variantName}`);
});

// Called when a 'win' is emitted, in this case by this.experimentRef.current.win()
emitter.addWinListener(function(experimentName, variantName) {
  console.log(
    `Variant ${variantName} of experiment ${experimentName} was clicked`
  );
});

Coordinate Multiple Components

Try it on JSFiddle

import React from 'react';
import { Experiment, Variant, emitter } from '@marvelapp/react-ab-test';

// Define variants in advance.
emitter.defineVariants('My Example', ['A', 'B', 'C']);

function Component1 = () => {
  return (
    <Experiment name="My Example">
      <Variant name="A">
        <div>Section A</div>
      </Variant>
      <Variant name="B">
        <div>Section B</div>
      </Variant>
    </Experiment>
  );
};

const Component2 = () => {
  return (
    <Experiment name="My Example">
      <Variant name="A">
        <div>Subsection A</div>
      </Variant>
      <Variant name="B">
        <div>Subsection B</div>
      </Variant>
      <Variant name="C">
        <div>Subsection C</div>
      </Variant>
    </Experiment>
  );
};

class Component3 extends React.Component {
  onButtonClick(e) {
    emitter.emitWin('My Example');
  }
  render() {
    return <button onClick={this.onButtonClick}>Emit a win</button>;
  }
}

const App = () => {
  return (
    <div>
      <Component1 />
      <Component2 />
      <Component3 />
    </div>
  );
};

// Called when the experiment is displayed to the user.
emitter.addPlayListener(function(experimentName, variantName) {
  console.log(`Displaying experiment ${experimentName} variant ${variantName}`);
});

// Called when a 'win' is emitted, in this case by emitter.emitWin('My Example')
emitter.addWinListener(function(experimentName, variantName) {
  console.log(
    `Variant ${variantName} of experiment ${experimentName} was clicked`
  );
});

Weighting Variants

Try it on JSFiddle

Use emitter.defineVariants() to optionally define the ratios by which variants are chosen.

import React from 'react';
import { Experiment, Variant, emitter } from '@marvelapp/react-ab-test';

// Define variants and weights in advance.
emitter.defineVariants('My Example', ['A', 'B', 'C'], [10, 40, 40]);

const App = () => {
  return (
    <div>
      <Experiment name="My Example">
        <Variant name="A">
          <div>Section A</div>
        </Variant>
        <Variant name="B">
          <div>Section B</div>
        </Variant>
        <Variant name="C">
          <div>Section C</div>
        </Variant>
      </Experiment>
    </div>
  );
}

Force variant calculation before rendering experiment

There are some scenarios where you may want the active variant of an experiment to be calculated before the experiment is rendered. To do so, use emitter.calculateActiveVariant(). Note that this method must be called after emitter.defineVariants()

import { emitter } from '@marvelapp/react-ab-test';

// Define variants in advance
emitter.defineVariants('My Example', ['A', 'B', 'C']);
emitter.calculateActiveVariant('My Example', 'userId');

// Active variant will be defined even if the experiment is not rendered
const activeVariant = emitter.getActiveVariant('My Example');

Debugging

The debugger attaches a fixed-position panel to the bottom of the <body> element that displays mounted experiments and enables the user to change active variants in real-time.

The debugger is wrapped in a conditional if(process.env.NODE_ENV === "production") {...} and will not display on production builds using envify.

Try it on JSFiddle

import React from 'react';
import { Experiment, Variant, experimentDebugger } from '@marvelapp/react-ab-test';

experimentDebugger.enable();

const App = () => {
  return (
    <div>
      <Experiment name="My Example">
        <Variant name="A">
          <div>Section A</div>
        </Variant>
        <Variant name="B">
          <div>Section B</div>
        </Variant>
      </Experiment>
    </div>
  );
}

Server Rendering

A <Experiment /> with a userIdentifier property will choose a consistent <Variant /> suitable for server side rendering.

See ./examples/isomorphic for a working example.

Example

The component in Component.jsx:

var React = require('react');
var Experiment = require('react-ab-test/lib/Experiment');
var Variant = require('react-ab-test/lib/Variant');

module.exports = React.createClass({
  propTypes: {
    userIdentifier: React.PropTypes.string.isRequired,
  },
  render: function() {
    return (
      <div>
        <Experiment
          name="My Example"
          userIdentifier={this.props.userIdentifier}
        >
          <Variant name="A">
            <div>Section A</div>
          </Variant>
          <Variant name="B">
            <div>Section B</div>
          </Variant>
        </Experiment>
      </div>
    );
  },
});

We use a session ID for the userIdentifier property in this example, although a long-lived user ID would be preferable. See server.js:

require('babel/register')({ only: /jsx/ });

var express = require('express');
var session = require('express-session');
var React = require('react');
var ReactDOMServer = require('react-dom/server');
var Component = require('./Component.jsx');
var abEmitter = require('@marvelapp/react-ab-test/lib/emitter');

var app = express();

app.set('view engine', 'ejs');

app.use(
  session({
    secret: 'keyboard cat',
    resave: false,
    saveUninitialized: true,
  })
);

app.get('/', function(req, res) {
  var reactElement = React.createElement(Component, {
    userIdentifier: req.sessionID,
  });
  var reactString = ReactDOMServer.renderToString(reactElement);
  abEmitter.rewind();
  res.render('template', {
    sessionID: req.sessionID,
    reactOutput: reactString,
  });
});

app.use(express.static('www'));

app.listen(8080);

Remember to call abEmitter.rewind() to prevent memory leaks.

An EJS template in template.ejs:

<!doctype html>
<html>
  <head>
    <title>Isomorphic Rendering Example</title>
  </head>
  <script type="text/javascript">
    var SESSION_ID = <%- JSON.stringify(sessionID) %>;
  </script>
  <body>
    <div id="react-mount"><%- reactOutput %></div>
    <script type="text/javascript" src="bundle.js"></script>
  </body>
</html>

On the client in app.jsx:

var React = require('react');
var ReactDOM = require('react-dom');
var Component = require('../Component.jsx');

var container = document.getElementById('react-mount');

ReactDOM.render(<Component userIdentifier={SESSION_ID} />, container);

With Babel

Code from ./src is written in JSX and transpiled into ./lib using Babel. If your project uses Babel you may want to include files from ./src directly.

Alternative Libraries

Please let us know about alternate libraries not included here.

Resources for A/B Testing with React

Please let us know about React A/B testing resources not included here.

API Reference

<Experiment />

Experiment container component. Children must be of type Variant.

  • Properties:
    • name - Name of the experiment.
      • Required
      • Type: string
      • Example: "My Example"
    • userIdentifier - Distinct user identifier. When defined, this value is hashed to choose a variant if defaultVariantName or a stored value is not present. Useful for server side rendering.
      • Optional
      • Type: string
      • Example: "7cf61a4521f24507936a8977e1eee2d4"
    • defaultVariantName - Name of the default variant. When defined, this value is used to choose a variant if a stored value is not present. This property may be useful for server side rendering but is otherwise not recommended.
      • Optional
      • Type: string
      • Example: "A"

<Variant />

Variant container component.

  • Properties:
    • name - Name of the variant.
      • Required
      • Type: string
      • Example: "A"

emitter

Event emitter responsible for coordinating and reporting usage. Extended from facebook/emitter.

emitter.emitWin(experimentName)

Emit a win event.

  • Return Type: No return value
  • Parameters:
    • experimentName - Name of an experiment.
      • Required
      • Type: string
      • Example: "My Example"

emitter.addActiveVariantListener([experimentName, ] callback)

Listen for the active variant specified by an experiment.

  • Return Type: Subscription
  • Parameters:
    • experimentName - Name of an experiment. If provided, the callback will only be called for the specified experiment.
      • Optional
      • Type: string
      • Example: "My Example"
    • callback - Function to be called when a variant is chosen.
      • Required
      • Type: function
      • Callback Arguments:
        • experimentName - Name of the experiment.
          • Type: string
        • variantName - Name of the variant.
          • Type: string

emitter.addPlayListener([experimentName, ] callback)

Listen for an experiment being displayed to the user. Trigged by the React componentWillMount lifecycle method.

  • Return Type: Subscription
  • Parameters:
    • experimentName - Name of an experiment. If provided, the callback will only be called for the specified experiment.
      • Optional
      • Type: string
      • Example: "My Example"
    • callback - Function to be called when an experiment is displayed to the user.
      • Required
      • Type: function
      • Callback Arguments:
        • experimentName - Name of the experiment.
          • Type: string
        • variantName - Name of the variant.
          • Type: string

emitter.addWinListener([experimentName, ] callback)

Listen for a successful outcome from the experiment. Trigged by the emitter.emitWin(experimentName) method.

  • Return Type: Subscription
  • Parameters:
    • experimentName - Name of an experiment. If provided, the callback will only be called for the specified experiment.
      • Optional
      • Type: string
      • Example: "My Example"
    • callback - Function to be called when a win is emitted.
      • Required
      • Type: function
      • Callback Arguments:
        • experimentName - Name of the experiment.
          • Type: string
        • variantName - Name of the variant.
          • Type: string

emitter.defineVariants(experimentName, variantNames [, variantWeights])

Define experiment variant names and weighting. Required when an experiment spans multiple components containing different sets of variants.

If variantWeights are not specified variants will be chosen at equal rates.

The variants will be chosen according to the ratio of the numbers, for example variants ["A", "B", "C"] with weights [20, 40, 40] will be chosen 20%, 40%, and 40% of the time, respectively.

  • Return Type: No return value
  • Parameters:
    • experimentName - Name of the experiment.
      • Required
      • Type: string
      • Example: "My Example"
    • variantNames - Array of variant names.
      • Required
      • Type: Array.<string>
      • Example: ["A", "B", "C"]
    • variantWeights - Array of variant weights.
      • Optional
      • Type: Array.<number>
      • Example: [20, 40, 40]

emitter.setActiveVariant(experimentName, variantName)

Set the active variant of an experiment.

  • Return Type: No return value
  • Parameters:
    • experimentName - Name of the experiment.
      • Required
      • Type: string
      • Example: "My Example"
    • variantName - Name of the variant.
      • Required
      • Type: string
      • Example: "A"

emitter.getActiveVariant(experimentName)

Returns the variant name currently displayed by the experiment.

  • Return Type: string
  • Parameters:
    • experimentName - Name of the experiment.
      • Required
      • Type: string
      • Example: "My Example"

emitter.calculateActiveVariant(experimentName [, userIdentifier, defaultVariantName])

Force calculation of active variant, even if the experiment is not displayed yet. Note: This method must be called after emitter.defineVariants

  • Return Type: string
  • Parameters:
    • experimentName - Name of the experiment.
      • Required
      • Type: string
      • Example: "My Example"
    • userIdentifier - Distinct user identifier. When defined, this value is hashed to choose a variant if defaultVariantName or a stored value is not present. Useful for server side rendering.
      • Optional
      • Type: string
      • Example: "7cf61a4521f24507936a8977e1eee2d4"
    • defaultVariantName - Name of the default variant. When defined, this value is used to choose a variant if a stored value is not present. This property may be useful for server side rendering but is otherwise not recommended.
      • Optional
      • Type: string
      • Example: "A"

emitter.getSortedVariants(experimentName)

Returns a sorted array of variant names associated with the experiment.

  • Return Type: Array.<string>
  • Parameters:
    • experimentName - Name of the experiment.
      • Required
      • Type: string
      • Example: "My Example"

emitter.setCustomDistributionAlgorithm(customAlgorithm)

Sets a custom function to use for calculating variants overriding the default. This can be usefull in cases when variants are expected from 3rd parties or when variants need to be in sync with other clients using ab test but different distribution algorithm.

  • Return Type: No return value
  • Parameters:
    • customAlgorithm - Function for calculating variant distribution.
      • Required
      • Type: function
      • Callback Arguments:
        • experimentName - Name of the experiment.
          • Required
          • Type: string
        • userIdentifier - User's value which is used to calculate the variant
          • Required
          • Type: string
        • defaultVariantName - Default variant passed from the experiment
          • Type: string

Subscription

Returned by the emitter's add listener methods. More information available in the facebook/emitter documentation.

subscription.remove()

Removes the listener subscription and prevents future callbacks.

  • Parameters: No parameters

experimentDebugger

Debugging tool. Attaches a fixed-position panel to the bottom of the <body> element that displays mounted experiments and enables the user to change active variants in real-time.

The debugger is wrapped in a conditional if(process.env.NODE_ENV === "production") {...} and will not display on production builds using envify. This can be overriden by setDebuggerAvailable

experimentDebugger.setDebuggerAvailable(isAvailable)

Overrides process.env.NODE_ENV check, so it can be decided if the debugger is available or not at runtime. This allow, for instance, to enable the debugger in a testing environment but not in production. Note that you require to explicitly call to .enable even if you forced this to be truthy.

  • Return Type: No return value
  • Parameters:
    • isAvailable - Tells whether the debugger is available or not
      • Required
      • Type: boolean

experimentDebugger.enable()

Attaches the debugging panel to the <body> element.

  • Return Type: No return value

experimentDebugger.disable()

Removes the debugging panel from the <body> element.

  • Return Type: No return value

mixpanelHelper

Sends events to Mixpanel. Requires window.mixpanel to be set using Mixpanel's embed snippet.

Usage

When the <Experiment /> is mounted, the helper sends an Experiment Play event using mixpanel.track(...) with Experiment and Variant properties.

When a win is emitted the helper sends an Experiment Win event using mixpanel.track(...) with Experiment and Variant properties.

Try it on JSFiddle

import React from 'react';
import { Experiment, Variant, mixpanelHelper } from '@marvelapp/react-ab-test';

// window.mixpanel has been set by Mixpanel's embed snippet.
mixpanelHelper.enable();

class App extends React.Component {

  experimentRef = React.createRef();

  onButtonClick(e) {
    this.experimentRef.current.win();
    // mixpanelHelper sends the 'Experiment Win' event, equivalent to:
    // mixpanel.track('Experiment Win', {Experiment: "My Example", Variant: "A"})
  }
  componentWillMount() {
    // mixpanelHelper sends the 'Experiment Play' event, equivalent to:
    // mixpanel.track('Experiment Play', {Experiment: "My Example", Variant: "A"})
  }
  render() {
    return (
      <div>
        <Experiment ref={this.experimentRef} name="My Example">
          <Variant name="A">
            <div>Section A</div>
          </Variant>
          <Variant name="B">
            <div>Section B</div>
          </Variant>
        </Experiment>
        <button onClick={this.onButtonClick}>Emit a win</button>
      </div>
    );
  }
}

mixpanelHelper.enable()

Add listeners to win and play events and report results to Mixpanel.

  • Return Type: No return value

mixpanelHelper.disable()

Remove win and play listeners and stop reporting results to Mixpanel.

  • Return Type: No return value

segmentHelper

Sends events to Segment. Requires window.analytics to be set using Segment's embed snippet.

Usage

When the <Experiment /> is mounted, the helper sends an Experiment Viewed event using segment.track(...) with experimentName and variationName properties.

When a win is emitted the helper sends an Experiment Won event using segment.track(...) with experimentName and variationName properties.

Try it on JSFiddle

import React from 'react';
import { Experiment, Variant, segmentHelper } from '@marvelapp/react-ab-test';

// window.analytics has been set by Segment's embed snippet.
segmentHelper.enable();

class App extends React.Component {
  experimentRef = React.createRef();

  onButtonClick(e) {
    this.experimentRef.current.win();
    // segmentHelper sends the 'Experiment Won' event, equivalent to:
    // segment.track('Experiment Won', {experimentName: "My Example", variationName: "A"})
  }
  componentWillMount() {
    // segmentHelper sends the 'Experiment Viewed' event, equivalent to:
    // segment.track('Experiment Viewed, {experimentName: "My Example", variationName: "A"})
  }
  render() {
    return (
      <div>
        <Experiment ref={this.experimentRef} name="My Example">
          <Variant name="A">
            <div>Section A</div>
          </Variant>
          <Variant name="B">
            <div>Section B</div>
          </Variant>
        </Experiment>
        <button onClick={this.onButtonClick}>Emit a win</button>
      </div>
    );
  }
}

segmentHelper.enable()

Add listeners to win and play events and report results to Segment.

  • Return Type: No return value

segmentHelper.disable()

Remove win and play listeners and stop reporting results to Segment.

  • Return Type: No return value

How to contribute

Requisites

Before contribuiting you need:

Then you can:

  • Apply your changes ๐Ÿ˜Ž
  • Build your changes with yarn build
  • Test your changes with yarn test
  • Lint your changes with yarn lint
  • And finally open the PR! ๐ŸŽ‰

Running Tests

yarn test

react-ab-test's People

Stargazers

 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  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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-ab-test's Issues

Showing only one variant, every-time refreshed

Table of Contents

Expected Behavior

show 50, 50 of 2 variants as assigned in defineVariants

emitter.defineVariants(
  "themeExperiment",
  ["darkTheme", "lightTheme"],
  [50, 50]
);

const Pride: React.SFC = () => {
  return (
    <Experiment name="themeExperiment">
      <Variant name="darkTheme">
        <DarkTheme />
      </Variant>
      <Variant name="lightTheme">
        <LightTheme />
      </Variant>
    </Experiment>
  );
};

Current Behavior

Only one variant being shown

Possible Solution

My console cries about using old react lifecycle methods.

Steps to Reproduce (for bugs)

Use one the examples and it does the same, after refreshing few times, it still shows same variant

Your Environment

  • Version used: ^2.3.0
  • Browser Name and version: Chrome Version 84.0.4147.105
  • Operating System and version (desktop or mobile): both
  • Framework: Next.js version 9

Using defaultVariantName for same experiment in multiple places breaks experiment

I have the following with a defaultVariantName defined in one component with the same experiment as another component. My app works with just one defined but if I want to define it again to make it consistent it breaks.

// component 1
<Experiment name="x" defaultVariantName="A">
  <Variant name="A" >
    <AIntro/>
  </Variant>
  <Variant name="B">
    <Intro/>
  </Variant>
</Experiment>

// component 2
<Experiment name="x">
  <Variant name="A">
    <AHeader/>
  </Variant>
  <Variant name="B">
     <Header/>
  </Variant>
</Experiment>

A suggestion is to define the defaultVariantName within the .defineVarients() method so it is defined where the other definitions are created
PushtellEventEmitter.prototype.defineVariants = function(experimentName, variantNames, variantWeights, defaultVariantName)

Default type using useExperiment

  • [Expected Behavior]
    There should be a default type when setting Variants so that if variant is not among the ones that are listed, default component/UI is shown
  • [Current Behavior]
  • Right now, it don't show anything

My requirements are only fulfilled using useExperiment hook and I can't find anything like defaultVariantName for this hook

Example for emitting events without Segment/Mixpanel

Thank you for releasing this tool for free! I think it's important to have a free alternative to a 30000โ‚ฌ/y Optimizely or a Google-owned Google Optimize.

I'd like to send an event (to an analytics tool and/or my db. I'm not using segment/mixpanel) depending on which component won. But I'm not sure how to do that. I'm also unsure as to how I'm able to define "won".

I just want an event that tells me: visitor/user with this ID experienced variant B of that component.
The component may have a button CTA inside it, eg if the component is an entire landing page.
That would be a "win" directly associated with the variant.
Regardless of that, there are other wins further down the line of the user journey that might be influenced.
So it's important to save, whether a Variant has been experienced.

It would be really helpful to see a code example (in the already very helpful Readme) of events being sent without segment/mixpanel.

  1. for when a variant mounted
  2. for when something in that variant happened.

Working with NextJS

Hey,

I'm looking how to I can use this wonderfull package with NextJS and especially with SSR.

I'm using userIdentifier like you said, but if I force the local value to A test, it renders B test and update the local value to B value instead of keeping A value.

Any idea how I can manage this ?

Thanks

React 17

Let me know if anyone forks this to get it working with react 17!

Support for Hooks?

Have folks investigated updating to prepare for React Lifecycle event deprecation, and supporting React Hooks?

Thanks for maintaining this fork.

userIdentifier causes infinite render loop

Table of Contents

Expected Behavior

The userIdentifier prop should not cause infinite render loop, when used on the client side.

Current Behavior

When the userIdentifier prop is present, it causes infinite render loop.

Possible Solution

Steps to Reproduce (for bugs)

https://codesandbox.io/s/white-firefly-o3bqr

Trigger a re-render with the button.

Context

Your Environment

  • Version used: 3.0.0-beta.0
  • Browser Name and version:
  • Operating System and version (desktop or mobile):
  • Link to your project:

isomorphic emitter related tests may not work well in newer react

While evaluating changes required for 17.x - it appears that the isomorphic tests expect emitters to fire during componentWillMount. SSR typically triggers this event, but it is deprecated. The porting documentation suggests that browser implementations should transition to componentDidMount - but in an SSR environment, that is not something that is triggered. The alternative is to emit an event in the constructor, but that seems like an anti-pattern. I would suggest that these tests should be changed to not use the emitter to determine what was rendered, but instead use an inspection of the rendered DOM.

emitter.once('play', (experimentName, variantName) => {

Option to use cookies instead of localstorage

Table of Contents

Expected Behavior

if local storage is a no-op for the given browser or is not present, try using a cookie or some other storage mechanism instead; can be placed behind a flag if that's not desirable.

Current Behavior

Current behavior forces local storage as the storage medium

Possible Solution

Would be great if we can use a cookie as a backup if localstorage doesn't work

Steps to Reproduce (for bugs)

not relevant

Context

Localstorage is a no-op in iOS private browsing, which we've found to be unfortunately common on our service; but cookies definitely work in that scenario.

Your Environment

  • Version used: 2.3.0
  • Browser Name and version: Mobile Safari
  • Operating System and version (desktop or mobile): iOS 13.7
  • Link to your project: private repo

typescript support for useExperiment

Expected Behavior

Support of useExperiment when

yarn add -D @types/marvelapp___react-ab-test

Current Behavior

not Supporting of useExperiment when

yarn add -D @types/marvelapp___react-ab-test

I wrote a type definition for myself so if anyone wants it just let me know.
but I would happy to have it out of the box.

thanks ๐Ÿป

NextJS Rendering

Table of Contents

Expected Behavior

The Experiment should render one variation and it should be displayed properly

Setup:

<Experiment name="An Experiment" userIdentifier={this.props.sessionId}> - sessionId comes from express-session
  <Variant name="Variation A">
    <div>
     {children} - children here represent actual elements not like a parent Component
    </div>
  </Variant>
  <Variant name="Variation B">
    <div>
     {children} - children here represent actual elements not like a parent Component
    </div>
  </Variant>
</Experiment>

Current Behavior

The Experiment does render a variation but it does not render the proper elements and its children.

Outcome:

{/* Rendered Properly */}
<div className="class">
 <p className="class-p">
  Hello P
</p>
 <p className="class-pd">
  Hello PD
</p>
</div>

{/* Current Results */}
<div className="class">
 <p className="class-p">
  Hello PD
</p>
 <p className="class-pd">
  Hello P
</p>
</div>

Possible Solution

Steps to Reproduce (for bugs)

  1. Start a new nextjs project
  2. Make a new Experiment in a page
  3. Add the sessionId from express-session to the Experiment userIdentifier prop
  4. See the results

Context

I can not show different variants properly if the rendering keeps breaking.

Your Environment

  • Version used: 2.3.0
  • Browser Name and version: Chrome 86.0.4240.75 or Firefox 81
  • Operating System and version (desktop or mobile): Manjaro
  • Link to your project: Can not provide.

AB test removed but still firing mixpanel events

Table of Contents

Expected Behavior

If I removed AB test from react app. No event should fire on analytic tool like mixpanel.

Current Behavior

I have removed AB test from react app. But it is still firing and I can see mixpanel event for that

Possible Solution

Steps to Reproduce (for bugs)

This issue is caused when you remove AB test from code and might be possible local storage of some users might not be updated and still have local storage for this Ab test.

Context

Your Environment

  • Version used: 3.1.0
  • Browser Name and version: Chrome version 110
  • Operating System and version (desktop or mobile): Both
  • Link to your project:

Typescript types

I'm working on creating a typings file for this project. I'm curious if you would like to include this in the project - or should I plan on uploading to DefinitelyTyped?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.