Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

flag

Package Overview
Dependencies
Maintainers
1
Versions
34
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

flag

Feature flagging made easy for React and Redux

  • 4.2.0-0
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
9K
increased by0.66%
Maintainers
1
Weekly downloads
 
Created
Source

Flag

Feature flagging made easy for React and Redux

yarn add flag

Motivation

Feature flagging is necessary for large client-side applications. They improve development speed and allow teams to test new features before they are stable. In order to WANT to use feature flags in an application, they should be VERY easy to add and remove. That means minimal boiler plate and no need to pass boolean props down through component hierarchy. Such a thing could be done with global variables; however, they live outside of the React/Redux lifecycle, making them more difficult to control. Instead, this library injects and then accesses feature flags directly from the React context without getting in your way.

Flag allows you to declare flags as either plain values or as functions. If a flag is a function then it is referred to as a computed flag. The function accepts one argument which is the flags object itself. You do not have to use computed flags, but they can be very convenient. For example,

const flags = {
  // properties can be nested objects
  features: {
    // they can be boolean
    useMyCoolNewThing: true
  },
  config: {
    // they can be strings
    apiUrl: "www.example.com/api"
  },
  // they can be numbers
  cool: 1,
  dude: 5,
  // they can be computed
  coolAndDude: flags => flags.cool + flags.dude,
  // they can be computed from other computed properties.
  // other computed properties are resolved for you, so that you do not
  // need to call it as a function.
  largeCoolAndDude: flags => flags.coolAndDude > 10
};

Getting Started

This library has strong TypeScript support as of v4. In order to get that support, you must initialize the flag library before using it.

createFlags

Creates React bindings for flags. You should only initialize one instance of this API. Does not take any value arguments, but takes one type argument T which is the shape of your resolved flags.

// flags.ts

import createFlags from "flag";

export type MyFlags = {
  features: {
    useMyCoolNewThing: boolean;
  };
  config: {
    apiUrl: string;
  };
  cool: number;
  dude: number;
  coolAndDude: number;
  largeCoolAndDude: boolean;
};

const { FlagsProvider, Flag, useFlag, useFlags } = createFlags<MyFlags>();

export { FlagsProvider, Flag, useFlag, useFlags };

createReduxBindings

You can also add support for Redux by importing createReduxBindings from flag/redux.

ArgsTypeRequiredDescription
providerFlagsProvider<T>trueProvider created by createFlags
// flags.ts

// ... the above

import createReduxBindings from "flag/redux";

const {
  setFlagsAction,
  getFlagsSelector,
  createFlagsReducer,
  ConnectedFlagsProvider
} = createReduxBindings(FlagsProvider);

export {
  setFlagsAction,
  getFlagsSelector,
  createFlagsReducer,
  ConnectedFlagsProvider
};

React API

For brevity, the type T in the section below refers to the shape of your resolved feature flags.

Computable

Generic type used to describe unresolved flags. Very useful when including functions are part of your flag definitions because function arguments can be inferred.

import { Computable } from "flag";

type MyFlags = {
  a: boolean;
  b: boolean;
  c: boolean;
};

const flags: Computable<MyFlags> = {
  a: true,
  b: false,
  // 👇 `flags` type checks!
  c: flags => flags.a && flags.b
};

FlagsProvider

Returned as part of createFlags(). React component that makes flags available to children through the Context API.

PropsTypeRequiredDescription
flagsComputable<T>trueAll pre-computed flags
childrenReactNodetrueReact children
// index.tsx

import { MyApplication } from "./app";
import { FlagsProvider, Flag } from "./flags";

const instance = (
  <FlagsProvider flags={flags}>
    <MyApplication />
  </FlagsProvider>
);

React.render(instance, document.querySelector("#app"));

Flag

Returned as part of createFlags(). Renders a some UI based on whether a flag is truthy or falsy. It's a glorified if statement 😬. Must be used in side of FlagsProvider.

PropsTypeRequiredDescription
namestring[]trueMust be a valid key path of T
childrenReactNodefalseReact children
render(flags: T) => ReactNodefalseFunction that returns a ReactNode
fallbackRender(flags: T) => ReactNodefalseFunction that returns a ReactNode
componentComponentType<{ flags: T }>falseReact Component with T as props
fallbackComponentComponentType<{ flags: T }>falseReact Component with T as props

Order of deciding which of these nodes to renders is as follows:

  • If the flag is truthy:
    • render children if defined
    • call render with T if defined or
    • call component with {flags: T} if defined else
    • return null
  • If the flag is falsy:
    • call fallbackRender with T if defined or
    • call fallbackComponent with { flags: T } if defined else
    • return null
<Flag
  name={["features", "useMyCoolNewThing"]}
  render={() => <div>Rendered on truthy</div>}
  fallbackRender={() => <div>Rendered on falsy</div>}
/>

useFlags

Returned as part of createFlags(). A React hook that returns all of the flags. Must be used in side of FlagsProvider.

// my-component.tsx

import { useFlags } from "./flags";

const MyComponent = () => {
  const flags = useFlags();

  return <div>The API url is "{flags.config.apiUrl}"</div>;
};

useFlag

Returned as part of createFlags(). A React hook to return a single flag. Must be used in side of FlagsProvider.

ArgsTypeRequiredDescription
keyPathstring[]trueMust be a valid key path of T
// my-component.tsx

import { useFlags } from "./flags";

const MyComponent = () => {
  const apiUrl = useFlag(["config", "apiUrl"]);

  return <div>The API url is "{apiUrl}"</div>;
};

Redux API

createFlagsReducer

Returned as part of createReduxBindings(...). Creates a reducer to be used in your Redux stores.

ArgsTypeRequiredDescription
flagsTtrueThe initial value of your flags
// reducer.ts

import { combineReducers } from "redux";
import { Computable } from "flag";
import { createFlagsReducer, MyFlags } from "./flags";
import { otherReducer } from "./other-reducer";

const flags: Computable<MyFlags> = {
  // ...
};

export default combineReducers({
  // 👇 must use the "flags" key of your state
  flags: createFlagsReducer(flags),
  other: otherReducer
});

getFlagsSelector

A selector to retrieve computed flags from Redux state. It is not enough to say state.flags because createFlagsReducer does not eagerly evaluate computable flags. (Though I suppose if you don't use any computable flags, then you don't necessarily need this 🤷‍♂️.)

// reducer.ts

import { getFlagsSelector, MyFlags } from "./flags";

type State = {
  flags: MyFlags;
  // ...
};

// ...

export const getFlags = (state: State) => getFlagsSelector(state);

ConnectedFlagsProvider

Returned as part of createReduxBindings(...). Wraps FlagsProvider, fetching the flags from Redux state.

import { Provider } from "redux";
import { MyApplication } from "./app";
import { ConnectedFlagsProvider } from "./flags";
import { store } from "./store";

const instance = (
  <Provider store={store}>
    <ConnectedFlagsProvider>
      <MyApplication />
    </ConnectedFlagsProvider>
  </Provider>
);

React.render(instance, document.querySelector("#app"));

setFlagsAction

Returned as part of createReduxBindings(...). A dispatchable action that sets flags. Merges a partial value of pre-computed flags with existing pre-computed flags.

ArgsTypeRequiredDescription
flagsComputable<T>truePartial pre-computed flags
import { Thunk } from "redux-thunk";
import { setFlagsAction } from "./flags";

export const someThunk: Thunk<any> = ({ dispatch }) => async () => {
  const user = await fetchUser();

  dispatch(setFlagsAction(user.flags));
  // ...
};

License

MPL-2.0

FAQs

Package last updated on 04 Jul 2019

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc