flagged
Version:
Feature flags for React made easy with hooks, HOC and Render Props
56 lines (55 loc) • 1.97 kB
JavaScript
import * as React from "react";
let FeatureFlagsContext = React.createContext({});
function transformFlags(features) {
if (!Array.isArray(features))
return features;
return Object.fromEntries(features.map((feature) => [feature, true]));
}
function mergeFeatures(a, b) {
return { ...a, ...b };
}
export function FlagsProvider({ features = {}, children, }) {
let currentFeatures = useFeatures();
return (React.createElement(FeatureFlagsContext.Provider, { value: mergeFeatures(transformFlags(currentFeatures), transformFlags(features)) }, children));
}
// Custom Hook API
export function useFeatures() {
return React.useContext(FeatureFlagsContext);
}
// Custom Hook API
export function useFeature(name) {
let features = useFeatures();
if (Array.isArray(features))
return features.includes(name);
if (typeof features[name] === "boolean")
return features[name];
let featureGroup = structuredClone(features);
for (let featureName of name.split("/")) {
if (typeof featureGroup === "boolean")
return featureGroup;
if (featureGroup[featureName] === undefined)
return false;
featureGroup = featureGroup[featureName];
}
return featureGroup;
}
// Render Prop API
export function Feature({ name, children, render = children, }) {
let hasFeature = useFeature(name);
if (typeof render === "function")
return render(hasFeature);
if (!hasFeature)
return null;
return React.createElement(React.Fragment, null, render);
}
// High Order Component API
export function withFeature(featureName) {
return (Component) => {
function WithFeature(props) {
return (React.createElement(Feature, { name: featureName },
React.createElement(Component, { ...props })));
}
WithFeature.displayName = `WithFeature(${Component.displayName || Component.name})`;
return WithFeature;
};
}