Last active
January 31, 2019 21:23
-
-
Save hartzis/3201700dd4eb61e1e97436fb158711b7 to your computer and use it in GitHub Desktop.
A/B Testing react, experiment(s)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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