Skip to content
This repository has been archived by the owner on Dec 7, 2022. It is now read-only.

Expose a higher-order component version of FeatureFlag. #33

Open
threehams opened this issue Aug 9, 2017 · 2 comments
Open

Expose a higher-order component version of FeatureFlag. #33

threehams opened this issue Aug 9, 2017 · 2 comments

Comments

@threehams
Copy link
Collaborator

The current API uses a component and callback which essentially forces the use of a class component. This is fine for smaller changes, but it doesn't compose well with other React HOC-based libraries.

An example with code splitting and conditional data fetching:

const CardGroup = compose(
  withFeatureFlag("useNewCard"),
  withRouter,
  fetchData("cards", props => ({ id: props.params.postId })),
  branch( // from recompose library
    props => props.featureFlags.useNewCard === "A",
    renderComponent(AsyncCardA), // code split
  ),
)(AsyncCardB); // code split

This can allow the A/B test to be completely transparent to the wrapped component, and it can be easily added or removed without adding custom wrapper components.

This should be possible to do while sharing code with the existing component, possibly as a simple wrapper.

@peterbee
Copy link

This is quite limited because it only allows for a single flag to be passed in, but it reuses the current FeatureFlag component to accomplish an HOC:

export default flagKey => WrappedComponent => {
  const _renderFeature = function(props, featureFlagValue = true) {
    const flags = {};
    flags[flagKey] = featureFlagValue;
    return <WrappedComponent {...props} featureFlags={flags} />;
  };

  return function(props) {
    return (
      <FeatureFlag
        flagKey={flagKey}
        // null flag value means LaunchDarkly is not initialized yet
        initialRenderCallback={_renderFeature.bind(this, props, null)}
        renderDefaultCallback={_renderFeature.bind(this, props, false)}
        renderFeatureCallback={_renderFeature.bind(this, props)}
      />
    );
  };
};

Thoughts?

@sethbattin
Copy link
Contributor

Thanks for resurrecting this issue @peterbee. I have one concern about your proposal:

return <WrappedComponent {...props} featureFlags={flags} />;

^- this tactic means that the wrapped component must comprehend a prop called featureFlags. That's undesirable because it means we can't take an existing feature, wrap it in the HOC (with no other changes), and see a different flagged outcome. Looking at @threehams's proposal via recompose, we do get that quality. The challenger component and the challengee(?) can be completely decoupled:

  1. start with existing feature
  2. invent challenger feature
  3. wrap existing feature usage with HOC that references challenger
  4. decide winner and delete the HOC

Although now that I'm looking twice at your code, it seems maybe that prop is superfluous anyway? We already know which callback was exercised by the featureFlagValue argument, so no need to pass the flag along to WrappedComponent. Right? We could still get an unmolested challengee in there.

Forgive me for making the same comment on both your posts, but please do submit a PR. It will make the code commenting a lot easier. 🙂

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants