Skip to content

Instantly share code, notes, and snippets.

@hartzis
Last active January 31, 2019 21:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hartzis/3201700dd4eb61e1e97436fb158711b7 to your computer and use it in GitHub Desktop.
Save hartzis/3201700dd4eb61e1e97436fb158711b7 to your computer and use it in GitHub Desktop.
A/B Testing react, experiment(s)
/*
* Component for displaying an experiment and bucket(s)
*/
/*******************************
* Option 1a - prop renders based on bucket names, ~ mimics <Flag />
*/
<Experiment
name="exp:test-message"
bucketA={({ recordInteraction }) => (<Message color="blue" onClick={() => recordInteraction('click')}>Welcome!</Message>)}
bucketB={({ recordInteraction }) => (<Message color="red" onClick={() => recordInteraction('click')}>Welcome!</Message>)}
bucketC={({ recordInteraction }) => (<Message color="cyan" onClick={() => recordInteraction('click')}>Welcome!</Message>)}
fallback={() => (<NotInExperiment />)} //optional
/>
// rough internal implementation for Option 1a
const RenderPropsExp = ({ bucket, recordEvent, fallback, ...rest }) => {
if (rest[bucket]) return rest[bucket]({ recordEvent });
if (fallback) return fallback();
return null;
};
// Option 1b
<Experiment name="exp:test-message">
{({ bucket, recordInteraction }) => {
if (bucket === 'bucketA') return (<BucketA onClick={() => recordInteraction('click')} />);
if (bucket === 'bucketB') return (<BucketB onClick={() => recordInteraction('click')} />);
// default/unassigned/fallback
return (<NotInExperiment />)
}}
</Experiment>
// rough internal implementation for Option 1b
const RenderChildrenExp = ({ bucket, recordInteraction, children }) => {
return children({ bucket, recordInteraction });
};
/*******************************
* Option 2 - Experiment and Bucket Child as Function?
*/
<Experiment name="exp:test-message" >
<Bucket name="bucketA">
{({ recordEvent }) => (<Message color="blue" onClick={() => recordEvent('click')}>Welcome!</Message>)}
</Bucket>
<Bucket name="bucketB">
{({ recordEvent }) => (<Message color="red" onClick={() => recordEvent('click')}>Welcome!</Message>)}
</Bucket>
<Fallback>{<span>{'fallback'}</span>}</Fallback>
</Experiment>
// rough internal implementation for Option 2
const RenderPropsExp = ({ bucket, triggerAction, children }) => {
let bucketRender = null;
let FallbackRender = null;
React.Children.forEach(children, (child) => {
if (child.props.name === bucket) bucketRender = child;
if (child.type === Fallback) FallbackRender = child;
});
if (buckerRender) return bucketRender.props.children({triggerAction});
if (fallback) return (<FallbackRender/>);
return null;
};
const Bucket = () => (null);
const Fallback = (props) => (<Fragment>{props.children}</Fragment>);
/*
* Supply url overrides
* ?exp=test-message&bucket=bucketA
*/
/*
* TODOS: follow up work
*/
/*
* HOC for retrieving and setting an Experiment prop
*/
const CardContainer = compose(
withExperiment('exp:test-button', 'testButton'), // optional second arg
withProps(({
testButton: { // 'testButton' becomes the prop key instead of ['exp:test-button']
bucket, // is user assigned a bucket.
recordImpression, // log impression for user for this experiment - () => fetchFootage('/wasabi/v1/impression/test-button')
recordInteraction, // log an experiment action with 'data'
bucketData, // if some bucket data was sent it is here. ¿"enforce" JSON?
},
}) => ({
showButton: !!bucketed,
buttonStyle: JSON.parse(bucketData), // ¿?
onButtonShown: () => recordImpression(),
onButtonClick: () => recordInteraction('click'),
})),
)(Card);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment