🎚Flip or flop features in your React application in real-time backed by flag provider of your choice 🚦
Switch branches/tags
v0.2.1 v0.2.0 v0.1.0 @flopflip/types@1.3.0 @flopflip/types@1.2.1 @flopflip/types@1.2.0 @flopflip/types@1.1.1 @flopflip/types@1.1.0 @flopflip/types@1.0.1 @flopflip/types@1.0.0 @flopflip/splitio-adapter@1.3.2 @flopflip/splitio-adapter@1.3.1 @flopflip/splitio-adapter@1.3.0 @flopflip/splitio-adapter@1.3.0-beta.4 @flopflip/splitio-adapter@1.3.0-beta.3 @flopflip/splitio-adapter@1.3.0-beta.2 @flopflip/splitio-adapter@1.3.0-beta.1 @flopflip/splitio-adapter@1.3.0-beta.0 @flopflip/splitio-adapter@1.2.16 @flopflip/splitio-adapter@1.2.16-beta.0 @flopflip/splitio-adapter@1.2.15 @flopflip/splitio-adapter@1.2.15-beta.0 @flopflip/splitio-adapter@1.2.14 @flopflip/splitio-adapter@1.2.13 @flopflip/splitio-adapter@1.2.12 @flopflip/splitio-adapter@1.2.11 @flopflip/splitio-adapter@1.2.10 @flopflip/splitio-adapter@1.2.9 @flopflip/splitio-adapter@1.2.8 @flopflip/splitio-adapter@1.2.7 @flopflip/splitio-adapter@1.2.6 @flopflip/splitio-adapter@1.2.5 @flopflip/splitio-adapter@1.2.4 @flopflip/splitio-adapter@1.2.3 @flopflip/splitio-adapter@1.2.2 @flopflip/splitio-adapter@1.2.1 @flopflip/splitio-adapter@1.2.0 @flopflip/splitio-adapter@1.1.0 @flopflip/splitio-adapter@1.0.2 @flopflip/splitio-adapter@1.0.1 @flopflip/splitio-adapter@1.0.0 @flopflip/react@6.1.6 @flopflip/react@6.1.5 @flopflip/react@6.1.4 @flopflip/react@6.1.3 @flopflip/react@6.1.2 @flopflip/react@6.1.1 @flopflip/react@6.1.0 @flopflip/react@6.1.0-beta.4 @flopflip/react@6.1.0-beta.3 @flopflip/react@6.1.0-beta.2 @flopflip/react@6.1.0-beta.1 @flopflip/react@6.1.0-beta.0 @flopflip/react@6.0.3 @flopflip/react@6.0.3-beta.0 @flopflip/react@6.0.2 @flopflip/react@6.0.2-beta.0 @flopflip/react@6.0.1 @flopflip/react@6.0.0 @flopflip/react@5.1.6 @flopflip/react@5.1.5 @flopflip/react@5.1.4 @flopflip/react@5.1.3 @flopflip/react@5.1.2 @flopflip/react@5.1.1 @flopflip/react@5.1.0 @flopflip/react@5.0.14 @flopflip/react@5.0.13 @flopflip/react@5.0.12 @flopflip/react@5.0.11 @flopflip/react@5.0.10 @flopflip/react@5.0.9 @flopflip/react@5.0.8 @flopflip/react@5.0.7 @flopflip/react@5.0.6 @flopflip/react@5.0.5 @flopflip/react@5.0.4 @flopflip/react@5.0.3 @flopflip/react@5.0.2 @flopflip/react@5.0.1 @flopflip/react@5.0.0 @flopflip/react@4.1.0 @flopflip/react@4.0.5 @flopflip/react@4.0.4 @flopflip/react@4.0.3 @flopflip/react@4.0.2 @flopflip/react@4.0.1 @flopflip/react@4.0.0 @flopflip/react@3.0.1 @flopflip/react@3.0.0 @flopflip/react@2.1.5 @flopflip/react@2.1.4 @flopflip/react@2.1.3 @flopflip/react@2.1.2 @flopflip/react@2.1.1 @flopflip/react@2.1.0 @flopflip/react@2.0.1 @flopflip/react@2.0.0 @flopflip/react@1.1.5 @flopflip/react@1.1.0
Nothing to show
Clone or download
renovate[bot] and tdeekens chore(deps): update dependency eslint-config-prettier to v3.3.0 (#519)
This PR contains the following updates:

| Package | Type | Update | Change | References |
|---|---|---|---|---|
| eslint-config-prettier | devDependencies | minor | `3.1.0` -> `3.3.0` | [source](https://renovatebot.com/gh/prettier/eslint-config-prettier) |

---

### Release Notes

<details>
<summary>prettier/eslint-config-prettier</summary>

### [`v3.3.0`](https://renovatebot.com/gh/prettier/eslint-config-prettier/blob/master/CHANGELOG.md#Version-330-2018-11-11)

[Compare Source](https://renovatebot.com/gh/prettier/eslint-config-prettier/compare/v3.2.0...v3.3.0)

-   Added: The [vue/html-self-closing] rule (as a [special
    rule][vue/html-self-closing-special]). Thanks to Yamagishi Kazutoshi ([@&#8203;ykzts](https://renovatebot.com/gh/ykzts))!

### [`v3.2.0`](https://renovatebot.com/gh/prettier/eslint-config-prettier/blob/master/CHANGELOG.md#Version-320-2018-11-10)

[Compare Source](https://renovatebot.com/gh/prettier/eslint-config-prettier/compare/v3.1.0...v3.2.0)

-   Added: Support for [eslint-plugin-vue].
-   Fixed: The CLI helper tool should now work in Node.js 6 with npm 3 again.
    Thanks to Grant Snodgrass ([@&#8203;meeber](https://renovatebot.com/gh/meeber))!
-   Improved: Updated documentation.

</details>

---

### Renovate configuration

📅 **Schedule**: "before 3am on Monday" (UTC).

🚦 **Automerge**: Enabled.

♻️ **Rebasing**: Whenever PR becomes conflicted, or if you modify the PR title to begin with "`rebase!`".

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

This PR has been generated by [Renovate Bot](https://renovatebot.com/gh/marketplace/renovate). View repository job log [here](https://renovatebot.com/dashboard#tdeekens/flopflip).
Latest commit 04b7422 Nov 12, 2018
Permalink
Failed to load latest commit information.
.circleci feat(circle): run tests on node@11 (#517) Nov 11, 2018
.github feat(react-broadcast): add typings and readme for useFeatureToggle (#515 Nov 9, 2018
demo refactor: to remove `recompose` (#516) Nov 10, 2018
packages refactor: to use tiny-warning over warning (#521) Nov 12, 2018
.editorconfig feat(initial): ⭐️ draft of flopflip Jul 23, 2017
.eslintignore chore(package): move from xo to eslint-plugin-xo Oct 27, 2017
.eslintrc.yaml chore: add issue templates and automerge config Sep 27, 2018
.flowconfig chore: downgrade flow for ci Oct 17, 2018
.gitattributes feat(initial): ⭐️ draft of flopflip Jul 23, 2017
.gitignore build: add coverage reports via codecov Jul 30, 2017
.jestrc.flow.json refactor: add flow jest runner Apr 1, 2018
.jestrc.lint.json chore: fix linting matching glob Feb 19, 2018
.jestrc.prettier.json fix: prettier runner Jun 10, 2018
.jestrc.test.json chore: add jest workspaces watch plugin Sep 23, 2018
.nvmrc chore: nvmrc to 10 Jul 16, 2018
.prettierignore chore: package use prettierrc and ignore file Sep 10, 2017
.prettierrc docs: improve intro Jun 9, 2018
.renovaterc.json chore: enable automerge on renovate Sep 26, 2018
.yarnrc chore(package.json): remove unnecessary `lerna bootstrap` command Jan 4, 2018
CONTRIBUTING.md chore: apply prettier May 28, 2018
LICENSE feat(initial): ⭐️ draft of flopflip Jul 23, 2017
babel.config.js feat(babel@7): migrate test and build setup to babel@7 Sep 2, 2018
codecov.yml chore: add yaml parsing to prettier Aug 5, 2018
commitlint.config.js fix(commitlint-config): fix parserPreset shape Jan 4, 2018
demo.gif feat(initial): ⭐️ draft of flopflip Jul 23, 2017
husky.config.js chore: update dependencies (#501) Sep 27, 2018
jest-runner-eslint.config.js refactor: add flow jest runner Apr 1, 2018
jest-runner-test.config.js feat(babel@7): migrate test and build setup to babel@7 Sep 2, 2018
jest.transform.js feat(babel@7): migrate test and build setup to babel@7 Sep 2, 2018
lerna.json chore: update dependencies Aug 12, 2018
lint-staged.config.js fix: lint-staged config Jun 9, 2018
logo.png chore: update logo Jul 6, 2018
package.json chore(deps): update dependency eslint-config-prettier to v3.3.0 (#519) Nov 12, 2018
readme.md refactor: to remove `recompose` (#516) Nov 10, 2018
rollup.config.js fix(ci): refactor bundling to exclude deps when not building umd (#507) Oct 27, 2018
yarn.lock chore(deps): update dependency eslint-config-prettier to v3.3.0 (#519) Nov 12, 2018

readme.md

Logo

🎚 flopflip - Feature Toggling 🚦

Toggle (flip or flop) features being stored in Redux or in a broadcasting system (through the context) via a set of React components or HoCs.

❤️ React · Redux · Jest · Prettier · Flow · Enzyme · ESLint · Babel · Lodash · Lerna · Rollup 🙏

CircleCI Status Codecov Coverage Status Known Vulnerabilities Made with Coffee

Embracing real-time feature toggling in your React application

Feature flagging with LaunchDarkly - Fun Fun Function



Want to see a demo? Logo

❯ Why you might use this.

In summary feature toggling simplifies and speeds up your development processes. You can ship software more often, release to specified target audiences and test features with users (not only internal staff) before releasing them to everyone.

With flopflip you get many options and ways to toggle features. More elaborate examples below. For now imagine you have a new feature which is not finished developing. However, UX and QA already need access to it. It's hidden by a <Link> component redirecting. To toggle it all you need is:

<ToggleFeature flag="featureFlagName">
  <Link to="url/to/new/feature" />
</ToggleFeature>

Having flopflip setup up you can now target users by whatever you decide to send to e.g. LaunchDarkly. This could be location, hashed E-Mails or any user groups (please respect your user's privicy).

Another example would be to show a <button> but disable it for users who should not have access to the feature yet:

<ToggleFeature flag="featureFlagName">
  {({ isFeatureEnabled }) => <button disabled={!isFeatureEnabled} onClick={this.handleClick}>Try out feature</button>}
<ToggleFeature/>

In both examples flags will update in realtime (depending on the adapter and provider) and the User Interface will update accordingly. If this sounds interesting to you, keep reading.

❯ Browser support

IE / Edge
IE / Edge
Firefox
Firefox
Chrome
Chrome
Safari
Safari
Opera
Opera
iOS Safari
iOS Safari
Chrome for Android
Chrome for Android
IE11, Edge last 2 versions last 2 versions last 2 versions last 2 versions last 2 versions last version

❯ Package Status

Package Version Dependencies Downloads
react react Version react Dependencies Status react Downloads
react-broadcast react-broadcast Version react-broadcast Dependencies Status react-broadcast Downloads
react-redux react-redux Version react-redux Dependencies Status react-redux Downloads
launchdarkly-adapter launchdarkly-adapter Version launchdarkly-adapter Dependencies Status launchdarkly-adapter Downloads
splitio-adapter splitio-adapter Version splitio-adapter Dependencies Status splitio-adapter Downloads
memory-adapter memory-adapter Version memory-adapter Dependencies Status memory-adapter Downloads
localstorage-adapter localstorage-adapter Version localstorage-adapter Dependencies Status localstorage-adapter Downloads
types types Version types Dependencies Status types Downloads

❯ Installation

This is a mono repository maintained using lerna. It currently contains five packages in a memory-adapter, a localstorage-adapter or launchdarkly-adapter, react, react-redux and react-broadcast. You should not need the launchdarkly-adapter yourself but one of our bindings (react-broadcast or react-redux). Both use the react package to share components.

Depending on the preferred integration (with or without redux) use:

yarn add @flopflip/react-redux or npm i @flopflip/react-redux --save

or

yarn add @flopflip/react-broadcast or npm i @flopflip/react-broadcast --save

❯ Demo

A minimal demo exists and can be adjusted to point to a custom LaunchDarkly account. You would have to create feature toggles according to the existing flags, though.

Then simply run:

  1. From the repositories root: yarn build:watch
  2. From /demo: first yarn and then yarn start

A browser window should open and the network tab should show feature flags being loaded from LaunchDarkly.

❯ Documentation

Flopflip allows you to manage feature flags through the notion of adapters (e.g. LaunchDarkly or LocalStorage) within an application written using React with or without Redux.

@flopflip/react-redux & @flopflip/react-broadcast API

  • ConfigureFlopFlip a component to configure flopflip with an adapter (alternative to the store enhancer)
  • ReconfigureFlopFlip a component to reconfigure flopflip with new user properties either merged or overwritting old properties (shouldOverwrite prop)
  • branchOnFeatureToggle a Higher-Order Component (HoC) to conditionally render components depending on feature toggle state
  • injectFeatureToggle a HoC to inject a feature toggle onto the props of a component
  • injectFeatureToggles a HoC to inject requested feature toggles from existing feature toggles onto the props of a component
  • ToggleFeature a component conditionally rendering its children based on the status of a passed feature flag <ToggleFeature> child based on the status of its passed feature flag
  • reducer and STATE_SLICE a reducer and the state slice for the feature toggle state
  • createFlopFlipEnhancer a redux store enhancer to configure flipflip and add feature toggle state to your redux store

Configuration

You can setup flopflip to work in two ways:

  1. Use React's Context (hidden for you) via @flopflip/react-broadcast
  2. Integrate with Redux via @flopflip/react-redux

Often using @flopflip/react-broadcast will be the easiest way to get started. You would just need to pick an adapter which can be any of the privided. Either just a memory-adapter or an integration with LaunchDarkly via launchdarkly-adapter will work. More on how to use ConfigureFlopFlip below.

Whenever you want the flag state to live in Redux you can use @flopflip/react-redux which can be setup in two variations itself

  1. Again using ConfigureFlopFlip for simpler use cases, or...
  2. or with a Redux store enhancer.

The store enhancer replaces ConfigureFlopflip for setup and gives the ability to pass in a preloadedState as default flags. For ConfigureFlopflip the default flags would be passed as a defaultFlags-prop.

Setup using Components

Setup is easiest using ConfigureFlopFlip which is available in both @flopflip/react-broadcast and @flopflip/react-redux. Feel free to skip this section whenever setup using a store enhancer (in a redux context) is preferred.

It takes the props:

  • The adapter which can be e.g. launchdarkly-adapter
    • An adapter should implement the following methods: configure and reconfigure which both must return a Promise as configuration can be an asynchronous task
  • The adapterArgs containing whatever the underlying adapter accepts
    • The user object is often the basis to identify an user to toggle features. The user object can contain any additional data.
    • The adapter will receive onFlagsStateChange and onStatusStateChange will should be invoked accordingly to notify react-broadcast and react-redux about flag and status changes
  • The shouldDeferAdapterConfiguration prop can be used to defer the initial configuration the adapter. This might be helpful for cases in which you want to wait for e.g. the key to be present within your root component and you do not want flopflip to generate a uuid for you automatically.
  • The defaultFlags prop object can be used to specify default flag values until an adapter responds or in case flags were removed
  • The localstorage-adapter and memory-adapter expose a named updateFlags export which eases updating flags and flushes them to all components via react-broadcast or react-redux

Whenever you do not want to have the state of all flags persisted in redux the minimal configuration for a setup with @flopflip/react-broadcast and LaunchDarkly would be nothing more than:

import { ConfigureFlopFlip } from '@flopflip/react-redux';
import adapter from '@flopflip/launchdarkly-adapter';
// or import adapter from '@flopflip/memory-adapter';
// or import adapter from '@flopflip/localstorage-adapter';

<ConfigureFlopFlip adapter={adapter} adapterArgs={{ clientSideId, user }}>
  <App />
</ConfigureFlopFlip>;

You can also pass render or children as a function to act differently based on the underlying adapter's ready state:

<ConfigureFlopFlip adapter={adapter} adapterArgs={{ clientSideId, user }}>
  {{isAdapterReady} => isAdapterReady ? <App /> : <LoadingSpinner />}
</ConfigureFlopFlip>;
<ConfigureFlopFlip
  adapter={adapter}
  adapterArgs={{ clientSideId, user }}
  render={() => <App />}
/>

Note that children will be called with a loading state prop while render will only be called when the adapter is ready. This behaviour mirrors the workings of <ToggleFeature>.

This variant of the ConfigureFlopFlip component form @flopflip/react-broadcast will use the context and a broadcasting system to reliably communicate with children toggling features (you do not have to worry about any component returning false from shouldComponentUpdate). If you're using @flopflip/react-broadcast you're done already.

Given your preference is to have the feature flag's state persisted in redux you would simply add a reducer when creating your store.

import { createStore, compose, applyMiddleware } from 'redux';
import {
  ConfigureFlopFlip,
  flopflipReducer,
  FLOPFLIP_STATE_SLICE
} from '@flopflip/react-redux';

// Maintained somewhere within your application
import user from './user';
import appReducer from './reducer';

const store = createStore(
  combineReducers({
    appReducer,
    [FLOPFLIP_STATE_SLICE]: flopflipReducer,
  }),
  initialState,
  compose(
    applyMiddleware(...),
  )
)

Setup through a Redux store enhancer

Another way to configure flopflip is using a store enhancer. For this a flopflip reducer should be wired up with a combineReducers within your application in coordination with the STATE_SLICE which is used internally too to manage the location of the feature toggle states. This setup eliminates the need to use ConfigureFlopFlip somewhere else in your application's component tree.

In context this configuration could look like

import { createStore, compose, applyMiddleware } from 'redux';
import {
  createFlopFlipEnhancer,
  flopflipReducer,

  // We refer to this state slice in the `injectFeatureToggles`
  // HoC and currently do not support a custom state slice.
  FLOPFLIP_STATE_SLICE
} from '@flopflip/react-redux';
import adapter from '@flopflip/launchdarkly-adapter';

// Maintained somewhere within your application
import user from './user';
import appReducer from './reducer';

const store = createStore(
  combineReducers({
    appReducer,
    [FLOPFLIP_STATE_SLICE]: flopflipReducer,
  }),
  initialState,
  compose(
    applyMiddleware(...),
    createFlopFlipEnhancer(
      adapter,
      {
        clientSideId: window.application.env.LD_CLIENT_ID,
        user
      }
    )
  )
)

Note that @flopflip/react-redux also exports a createFlopflipReducer(preloadedState: Flags). This is useful when you want to populate the redux store with initial values for your flags.

Example:

const defaultFlags = { flagA: true, flagB: false };

combineReducers({
  appReducer,
  [FLOPFLIP_STATE_SLICE]: createFlopflipReducer(defaultFlags),
});

This way you can pass defaultFlags as the preloadedState directly into the flopflipReducer. This means you do not need to keep track of it in your applications's initialState as in the following anti-pattern example:

const initialState = {
  [FLOPFLIP_STATE_SLICE]: { flagA: true, flagB: false },
};
const store = createStore(
  // ...as before
  initialState
  // ...as before
);

Syncing the store reducer with adapters

In addition to initiating flopflip when creating your store, you could still wrap most or all of your application's tree in ConfigureFlopFlip. This is needed when you want to identify as a user and setup the integration with LaunchDarkly or any other flag provider or adapter.

Note: This is not needed when using the memory-adapter.

import adapter from '@flopflip/launchdarkly-adapter';

<ConfigureFlopFlip adapter={adapter} adapterArgs={{ clientSideId, user }}>
  <App />
</ConfigureFlopFlip>;

Whenever your application "gains" certain information (e.g. with react-router) only further down the tree but that information should be used for user targeting (through adapterArgs.user) you can use ReconfigureFlopflip. ReconfigureFlopflip itself communicates with ConfigureFlopflip to reconfigure the given adapter for more fine grained targeting with the passed user. You also do not have to worry about rendering any number of ReconfigureFlopflips before the adapter is initialized (e.g. LaunchDarkly). Requested reconfigurations will be queued and processed once the adapter is ready.

Imagine having ConfigureFlopflip above a given component wrapped by a Route:

<ConfigureFlopFlip adapter={adapter} adapterArgs={{ clientSideId, user }}>
  <React.Fragment>
    <SomeOtherAppComponent />
    <Route
      exact={false}
      path="/:projectKey"
      render={routerProps => (
        <React.Fragment>
          <MyRouteComponent />
          <ReconfigureFlopflip
            // Note: This is the default - feel free to omit unless you want to set it to `true`.
            shouldOverwrite={false}
            // Note: this should be memoised to not trigger wasteful `reconfiguration`s.
            user={{ projectKey: routerProps.projectKey }}
          />
        </React.Fragment>
      )}
    />
  </React.Fragment>
</ConfigureFlopFlip>

Internally, ReconfigureFlopFlip will pass the projectKey to ConfigureFlopFlip, causing the adapter to automatically update the user context and therefore to flush new flags from the adapter (given they are provided by e.g. LaunchDarkly).

Note: Whenever shouldOverwrite is true the existing user configuration will be overwritten not merged. Use with care as any subsequent shouldOverwrite={true} will overwrite any previously passed user with shouldOverwrite={false} (default).

@flopflip/react-broadcast @flopflip/react-redux API

Apart from ConfigureFlopFlip both packages @flopflip/react-broadcast and @flopflip/react-redux export the same set of components to toggle based on features. Only the import changes depending on if you chose to integrate with redux or without. Again, behind the scenes the build on @flopflip/react to share common logic.

  • branchOnFeatureToggle a Higher-Order Component (HoC) to conditionally render components depending on feature toggle state
  • injectFeatureToggle a HoC to inject a feature toggle onto the props of a component
  • injectFeatureToggles a HoC to inject requested feature toggles from existing feature toggles onto the props of a component
  • ToggleFeature a component conditionally rendering its children based on the status of a passed feature flag

Note: that all passed flagNames passed as flag are a string. Depending on the adapter used these are normalized to be camel cased. This means that whenever a foo-flag-name is configured in e.g. LaunchDarkly or splitio it will have to be specified as fooFlagName. The same applies for a foo_flag_name. This is meant to help using flags in an adapter agnostic way. Whenever a flag is otherwise passed in the non-normalized form it is likely to default to false which is unintended in most cases. Lastly, flopflip will show a warning message in the console in development mode whenever a non normalized flag name is passed.

ToggleFeature

The component renders its children depending on the state of a given feature flag. It also allows passing an optional untoggledComponent which will be rendered whenever the feature is disabled instead of null.

import React, { Component } from 'react';
import { ToggleFeature } from '@flopflip/react-redux';
// or import { ToggleFeature } from '@flopflip/react-broadcast';
import flagsNames from './feature-flags';

const UntoggledComponent = () => <h3>{'At least there is a fallback!'}</h3>;
export default (
  <ToggleFeature
    flag={flagsNames.THE_FEATURE_TOGGLE}
    untoggledComponent={UntoggledComponent}
  >
    <h3>I might be gone or there!</h3>
  </ToggleFeature>
);

or with for multi variate feature toggles:

const UntoggledComponent = () => <h3>{'At least there is a fallback!'}</h3>;

export default (
  <ToggleFeature
    flag={flagsNames.THE_FEATURE_TOGGLE.NAME}
    variation={flagsNames.THE_FEATURE_TOGGLE.VARIATES.A}
    untoggledComponent={UntoggledComponent}
  >
    <h3>I might be gone or there!</h3>
  </ToggleFeature>
);

or with toggledComponent prop:

const UntoggledComponent = () => <h3>{'At least there is a fallback!'}</h3>;
const ToggledComponent = () => <h3>{'I might be gone or there!'}</h3>;

export default (
  <ToggleFeature
    flag={flagsNames.THE_FEATURE_TOGGLE.NAME}
    variation={flagsNames.THE_FEATURE_TOGGLE.VARIATES.A}
    untoggledComponent={UntoggledComponent}
    toggledComponent={ToggledComponent}
  />
);

or with Function as a Child (FaaC) which is always invoked with an isFeatureEnabled argument:

const UntoggledComponent = () => <h3>{'At least there is a fallback!'}</h3>;

export default (
  <ToggleFeature
    flag={flagsNames.THE_FEATURE_TOGGLE.NAME}
    variation={flagsNames.THE_FEATURE_TOGGLE.VARIATES.A}
    untoggledComponent={UntoggledComponent}
  >
       {({ isFeatureEnabled }) => <h3>I might be gone or there!</h3>}
  </ToggleFeature>
);

or with a render prop. Note that the render prop will only be invoked then the feature is turned on:

const UntoggledComponent = () => <h3>{'At least there is a fallback!'}</h3>;

export default (
  <ToggleFeature
    flag={flagsNames.THE_FEATURE_TOGGLE.NAME}
    variation={flagsNames.THE_FEATURE_TOGGLE.VARIATES.A}
    untoggledComponent={UntoggledComponent}
    render={() => <h3>I might be gone or there!</h3>}
  />
);

this last example will always turn the feature on if the variation or toggle does not exist. For this also look at defaultFlags for ConfigureFlopFlip.

We actually recommend maintaining a list of constants with feature flag names somewhere within your application. This avoids typos and unexpected behavior. After all, the correct workings of your feature flags is crutial to your application.

branchOnFeatureToggle({ flag: String, variation?: String | Boolean })

A HoC to conditionally render a component based on a feature toggle's state. It accepts the feature toggle name and an optional component to be rendered in case the feature is disabled.

Without a component rendered in place of the ComponentToBeToggled:

import { branchOnFeatureToggle } from '@flopflip/react-redux';
import flagsNames from './feature-flags';

const ComponentToBeToggled = () => <h3>I might be gone or there!</h3>;

export default branchOnFeatureToggle({ flag: flagsNames.THE_FEATURE_TOGGLE })(
  ComponentToBeToggled
);

With a component rendered in place of the ComponentToBeToggled:

import { branchOnFeatureToggle } from '@flopflip/react-redux';
import flagsNames from './feature-flags';

const ComponentToBeToggled = () => <h3>I might be gone or there!</h3>;
const ComponentToBeRenderedInstead = () => (
  <h3>At least there is a fallback!</h3>
);

export default branchOnFeatureToggle(
  { flag: flagsNames.THE_FEATURE_TOGGLE },
  ComponentToBeRenderedInstead
)(ComponentToBeToggled);

or when the flag is multi variation

import { branchOnFeatureToggle } from '@flopflip/react-redux';
import flagsNames from './feature-flags';

const ComponentToBeToggled = () => <h3>I might be gone or there!</h3>;
const ComponentToBeRenderedInstead = () => (
  <h3>At least there is a fallback!</h3>
);

export default branchOnFeatureToggle(
  {
    flag: flagsNames.THE_FEATURE_TOGGLE,
    variation: 'variate1',
  },
  ComponentToBeRenderedInstead
)(ComponentToBeToggled);

injectFeatureToggles(flagNames: Array<String>, propKey?: String, areOwnPropsEqual?: Function)

This HoC matches feature toggles given against configured ones and injects the matching result.

import { injectFeatureToggles } from '@flopflip/react-redux';
import flagsNames from './feature-flags';

const Component = props => {
  if (props.featureToggles[flagsNames.TOGGLE_A])
    return <h3>Something to render!</h3>;
  else if (props.featureToggles[flagsNames.TOGGLE_B])
    return <h3>Something else to render!</h3>;

  return <h3>Something different to render!</h3>;
};

export default injectFeatureToggles([flagsNames.TOGGLE_A, flagsNames.TOGGLE_B])(
  Component
);

injectFeatureToggle(flag: String, propKey?: String)

This HoC matches feature toggles given against configured ones and injects the matching result. branchOnFeatureToggle uses this to conditionally render a component. You also may pass a second argument to overwrite the default propKey of the injected toggle (defaults to isFeatureEnabled).

import { injectFeatureToggle } from '@flopflip/react-redux';
import flagsNames from './feature-flags';

const Component = props => {
  if (props.isFeatureEnabled) return <h3>Something to render!</h3>;

  return <h3>Something different to render!</h3>;
};

export default injectFeatureToggle(flagsNames.TOGGLE_B)(Component);

The feature flags will be available as props within the component allowing some custom decisions based on their value.

Additional @flopflip/react-redux API

We also expose our internal selectors to access feature toggle(s) directly so that the use of injectFeatureToggle or injectFeatureToggles is not enforced or the only value to access flags from @flopflip/react-redux's store slice. The two selectors selectFeatureFlag and selectFeatureFlags return the same values for flags as injectFeatureToggle and injectFeatureToggles would.

An example usage for a connected component would be:

import { selectFeatureFlag } from '@flopflip/react-redux';

const mapStateToProps = state => ({
  someOtherState: state.someOtherState,
  isFeatureOn: selectFeatureFlag('fooFlagName')(state),
});

export default connect(mapStateToProps)(FooComponent);

as an alternative to using injectFeatureToggle:

const mapStateToProps = state => ({
  someOtherState: state.someOtherState,
})

export default compose(
  injectFeatureToggle('fooFlagName')
  connect(mapStateToProps)
)(FooComponent)

The same example above applies for selectFeatureFlags.

createFlopFlipEnhancer

Requires arguments of clientSideId:string, user:object.

  • The adapter
  • The adapterArgs object
    • Often with the before mentioned user object user object which often needs at least a key attribute

Module formats

@flopflip/react-redux and @flopflip/react-broadcast is built for UMD (un- and minified) and ESM using rollup.

Both our @flopflip/launchdarkly-wrapper and @flopflip/react packages are "only" build for ESM and CommonJS (not UMD) as they are meant to be consumed by a module loader to be integrated.

The package.json files contain a main and module entry to point to a CommonJS and ESM build.

  • ...ESM just import the dist/@flopflip/<package>.es.js within your app.
    • ...it's a transpiled version accessible via the pkg.module
  • ...CommonJS use the dist/@flopflip/<package>.cjsjs
  • ...AMD use the dist/@flopflip/<package>.umd.js
  • ...<script /> link it to dist/@flopflip/<package>.umd.js or dist/@flopflip/<package>.umd.min.js

All build files are part of the npm distribution using the files array to keep install time short.

Also feel free to use unpkg.com as a CDN to the dist files.