Web Developer
Currently coding Daffy.org
Previously @prmkr, @platzi (YC W15), @vercel, and @able_co.
Organizer of @techtalks_pe.
Repos are my own.
Feature Flags for React made easy with hooks, HOC and Render Props
Home Page: https://github.com/sergiodxa/flagged
License: MIT License
Hello! Thank you for sharing your project, it's great! I just have a suggestion to display the fallback case from a function like 'render'.
The writing of a 'renderFallback' prop:
A function to execute in case the call to 'useFeature(name: string)' returns false inside the 'Feature' component.
export function Feature
(...)
const hasFeature = useFeature(name);
+ if (!hasFeature && typeof renderFallback === 'function') return renderFallback();
if (typeof render === 'function') return render(hasFeature);
return render;
We would implement an optional renderFallback prop with null starting value here:
export function Feature({
name,
children,
render = children,
+ renderFallback = null
}:{
name: string;
children?:
| React.ReactNode
| ((hasFeature: boolean | FeatureGroup) => JSX.Element);
render?:
| React.ReactNode
| ((hasFeature: boolean | FeatureGroup) => JSX.Element);
+ renderFallback?:
+ | React.ReactNode
+ | (() => JSX.Element);
})
then return the renderFallback function value after validating hasFeature here:
const hasFeature = useFeature(name);
+if (!hasFeature && typeof renderFallback === 'function') return renderFallback();
if (typeof render === 'function') return render(hasFeature);
return render;
+ {/* Render and Fallback Prop */}
+ <Feature name="v3" render={<V3Component />} renderFallback={<p>You are not in V3.</p>} />
Thanks for reading.
When I have code that looks like this:
import { useFeature } from "flagged";
export function Header() {
const hasV2 = useFeature("v2");
return <header>{hasV2 ? <h1>My App v2</h1> : <h1>My App v1</h1>}</header>;
}
createRoot(document.getElementById("root")!).render(
<FlagsProvider features={{ v3: true, moderate: false }}>
<Header />
</FlagsProvider>,
);
We get an exception thrown from this split method
Would love for it to essentially default to the flag being off if it can't find the flag in the provider and then do something like a console.warn
to let me know that the flag is missing rather than throwing a 'cannot split on undefined' error message.
Check if the flag doesn't exit
If the flag requested by the hook doesn't exist, treat it as if the flag were disabled and then throw a warning into the console
Software | Version |
---|---|
Flagged | ^2.0.8 |
React | 17 |
Browser | Chrome |
npm/yarn | npm |
Looking at the code I don't see why the case of feature used in an object would change?
Are you able to highlight why
works: <FlagsProvider features={{ test_feature: true } || {}}>
and does not work: <FlagsProvider features={{ testFeature: true } || {}}>
First of all, I like how simple the lib is also keeping it useful. 👍
My question is, do you have any plans to support more complex flags object?
I have an example.
I know I could use this example, but I don't need everything from that lib.
Let me know, I can open a PR for this.
Any plans to support provider chaining? I have a use case which some points of the application have additional features or change exiting ones.
I've tried to implement a wrapper around FlagsProvider
using useFeatures
but it throws an error if there's no provider declared. I've wrapped with a try catch but heard that it's not the best practice for hooks.
Example:
<FlagsProvider features={{ a: { enabled: true }, b: { enabled: true } }}>
<FlagsProvider features={{ a: { enabled: false } }}>
// .. here I'd receive a.enabled === false and b.enabled === true
</FlagsProvider>
</FlagsProvider>
I have three ideas:
useFeatures
; orLet me know if this could be something useful for you and I can open a PR.
This throws an error with React 18.2.0:
Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.
at resolveDispatcher (react.development.js:1476:1)
at useContext (react.development.js:1484:1)
at useFeatures (flagged.esm.js:44:20)
at FlagsProvider (flagged.esm.js:37:1)
at renderWithHooks (react-dom.development.js:16305:1)
...
This is the code I have for dist/flagged.esm
lines 33-45:
function FlagsProvider(_ref) {
var _ref$features = _ref.features,
features = _ref$features === void 0 ? {} : _ref$features,
children = _ref.children;
var currentFeatures = useFeatures();
return createElement(FeatureFlagsContext.Provider, {
value: mergeFeatures(transformFlags(currentFeatures), transformFlags(features))
}, children);
} // Custom Hook API
function useFeatures() {
return useContext(FeatureFlagsContext);
} // Custom Hook API
No error. I had no error with React 17.0.2.
After upgrading to React 18, the Auth0 Provider for v1.1.12 also gave the same error. Upgrading that to v2 fixed the error. So if you look at the https://github.com/auth0/auth0-react library provider changes between v1 and v2 that might give an idea for a fix.
Software | Version |
---|---|
Flagged | 2.0.9 |
React | 18.2.0 |
Browser | FF v116 |
npm/yarn | 8.19.4 |
I've noticed that older browsers (e.g., previous version of the Edge browser with the Edge engine) get the following error Object doesn’t support property or method "fromEntries"
.
Let's say I have a react js Class Component and want to get access to the list of features that were passed to <FlagsProvider />
. How can I achieve that?
The useFeatures()
hook gives me what I want but we can't use hooks in Class Components.
Let me know if it is clear.
Congrats on the library, very nice!
Hi, thanks for all your work on this package :)
I might have found a documentation issue or perhaps a bug?
Copying this from the examples in the README:
<Feature name="v2">
{(isEnabled: boolean) =>
isEnabled ? <h1>My App v2</h1> : <h1>My App v1</h1>
}
</Feature>;
Gives me this TypeScript warning:
Type '(isEnabled: boolean) => JSX.Element' is not assignable to type 'ReactNode | ((hasFeature: boolean | FeatureGroup) => Element)'.
Type '(isEnabled: boolean) => JSX.Element' is not assignable to type '(hasFeature: boolean | FeatureGroup) => Element'.
Types of parameters 'isEnabled' and 'hasFeature' are incompatible.
Type 'boolean | FeatureGroup' is not assignable to type 'boolean'.
Type 'FeatureGroup' is not assignable to type 'boolean'.ts(2322)
My TS skills aren't good enough to debug this myself but I got the sense that perhaps a feature may have been added since the examples were added?
This gives no warning, but I'm not sure I'm handling the possibility of isEnabled
being a FeatureGroup
here:
<Feature name="v2">
{(isEnabled) => (isEnabled ? <h1>My App v2</h1> : <h1>My App v1</h1>)}
</Feature>;
Hello there!
how can I validate multiple feature names with the Feature component?
Basically I want to do this:
<Feature names={[feature1, feature2]}>
</Feature>
To enable the render only if all features are enabled
I'd like to have an option to use the Feature wrapper component for elements when a feature is disabled. That would help to easier build alternative route when features are not enabled.
Possible solution example might be:
<FlagsProvider features={{ myFlag: true }}>
<Feature name="myFlag">
// feature enabled render part
</Feature>
<Feature name="myFlag" disabled>
// feature disabled render part
</Feature>
</FlagsProvider>
Another idea could be a different Wrapper like NotFeature:
<FlagsProvider features={{ myFlag: true }}>
<Feature name="myFlag">
// feature enabled render part
</Feature>
<NotFeature name="myFlag">
// feature disabled render part
</NotFeature>
</FlagsProvider>
PS: I see that there is the option to go via the render attribute on Feature but the above requested solutions would pay in as convenience options.
Webstorm throws warnings about unfined variables: "Unresolved variable isEnabled"
const FEATURE_FLAGS = useFeatures();
if (FEATURE_FLAGS.FEATURE_SEND_USER_CODES?.isEnabled) {
console.log('FEATURE_SEND_USER_CODES is enabled');
}
Is there any way to hide it?
I've added // noinspection JSUnresolvedVariable
but that's a pain.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.