Feature Flags

Enabling Feature flags in React using Redux

  1. // createFeatureFlaggedContainer.js
  2. import React from 'react';
  3. import { connect } from 'react-redux';
  4. import { isFeatureEnabled } from './reducers'
  5. export default function createFeatureFlaggedContainer({
  6. featureName,
  7. enabledComponent,
  8. disabledComponent
  9. }) {
  10. function FeatureFlaggedContainer({ isEnabled, ...props }) {
  11. const Component = isEnabled ? enabledComponent : disabledComponent;
  12. if (Component) {
  13. return <Component {...props} />;
  14. }
  15. // `disabledComponent` is optional property
  16. return null;
  17. }
  18. // Having `displayName` is very useful for debugging.
  19. FeatureFlaggedContainer.displayName = `FeatureFlaggedContainer(${ featureName })`;
  20. return connect((store) => {
  21. isEnabled: isFeatureEnabled(store, featureName)
  22. })(FeatureFlaggedContainer);
  23. }
  1. // EnabledFeature.js
  2. import { connect } from 'react-redux';
  3. import { isFeatureEnabled } from './reducers'
  4. function EnabledFeature({ isEnabled, children }) {
  5. if (isEnabled) {
  6. return children;
  7. }
  8. return null;
  9. }
  10. export default connect((store, { name }) => {
  11. isEnabled: isFeatureEnabled(store, name)
  12. })(EnabledFeature);
  1. // featureEnabled.js
  2. import createFeatureFlaggedContainer from './createFeatureFlaggedContainer'
  3. // Decorator for "Page" components.
  4. // usage: enabledFeature('unicorns')(UnicornsPage);
  5. export default function enabledFeature(featureName) {
  6. return (Component) => {
  7. return createFeatureFlaggedContainer({
  8. featureName,
  9. enabledComponent: Component,
  10. disabledComponent: PageNotFound, // 404 page or something similar
  11. });
  12. };
  13. };
  1. // features.js
  2. // This is quite simple reducer, containing only an array of features.
  3. // You can attach this data to a `currentUser` or similar reducer.
  4. // `BOOTSTAP` is global action, which contains the initial data for a page
  5. // Features access usually don't change during user usage of a page
  6. const BOOTSTAP = 'features/receive';
  7. export default function featuresReducer(state, { type, payload }) {
  8. if (type === BOOTSTAP) {
  9. return payload.features || [];
  10. }
  11. return state || [];
  12. }
  13. export function isFeatureEnabled(features, featureName) {
  14. return features.indexOf(featureName) !== -1;
  15. }
  1. // reducers.js
  2. // This is your main reducer.js file
  3. import { combineReducers } from 'redux';
  4. import features, { isFeatureEnabled as isFeatureEnabledSelector } from './features';
  5. // ...other reducers
  6. export default combineReducers({
  7. features
  8. // ...other reducers
  9. });
  10. // This is the important part, access to `features` reducer should only happens
  11. // via this selector.
  12. // Then you can always change where/how the features are stored.
  13. export function isFeatureEnabled({ features }, featureName) {
  14. return isFeatureEnabledSelector(features, featureName);
  15. }