toggled
v2.1.0
Published
Tiny library to use feature flags on React
Downloads
18
Maintainers
Readme
Tiny library to use feature flags in React. Get features by its slug identifier or get a binary output using flag queries.
Installation
NPM:
npm i toggled
Yarn:
yarn add toggled
API
DefaultFeature
Interface
Internal public interface used by default to type the <FeatureProvider />
and useFeature
.
Specification
export interface DefaultFeature {
slug: string
}
Example
// src/types/toggled.d.ts
import 'toggled'
// extend the interface if needed
declare module 'toggled' {
export interface DefaultFeature {
slug: string
settings?: any
}
}
FlagQuery
Type
It could be the feature slug or an flag queries array or more powerful, an object query.
Specification
type FlagQuery =
| string
| FlagQuery[]
| {
$or: FlagQuery[]
}
| {
$and: FlagQuery[]
}
| {
[slug: string]: boolean
}
Example
// src/constants/domain.ts
import { Op } from 'toggled'
// Note that each entry is a `FlagQuery`
export const flagQueries: Record<string, FlagQuery> = {
// True if the slug is in the context
FF_1: 'ff-1',
// True if all the slugs are in the context
FF_2_FULL: ['ff-2.1', 'ff-2.2'],
// True if `'ff-2.1'` is in the context and `'ff-2.2'` is not
FF_2_1_ONLY: {
'ff-2.1': true,
'ff-2.2': false,
},
// True if `'ff-3.1'` **or** `'ff-3.2'` is in the context
FF_3_X: {
[Op.OR]: ['ff-3.1', 'ff-3.2'],
},
// True if `'ff-4.1'` **and** `'ff-4.2'` are in the context
FF_4_FULL: {
[Op.AND]: ['ff-4.1', 'ff-4.2'],
},
// True if all the previous queries are true
COMPLEX: {
FF_1: 'ff-1',
FF_2_FULL: ['ff-2.1', 'ff-2.2'],
FF_2_1_ONLY: {
'ff-2.1': true,
'ff-2.2': false,
},
FF_3_X: {
[Op.OR]: ['ff-3.1', 'ff-3.2'],
},
FF_4_FULL: {
[Op.AND]: ['ff-4.1', 'ff-4.2'],
},
},
}
FeatureContext
Library context, exported for no specific reason, avoid using it and prefer the custom hooks, or open a PR to add a new one that obligates you to use the FeatureContext
.
Specification
interface FeatureContextValue<F extends DefaultFeature = DefaultFeature> {
cache: Map<string, F>
}
<FeatureProvider />
Provider component that exposes the features in a more convenient way to get them by its own slugs.
Specification
interface FeatureProviderProps<F extends DefaultFeature = DefaultFeature> {
features: F[]
children: React.ReactNode
}
Example
import { FeatureProvider } from 'toggled'
import apiClient from './api-client'
import App from './app'
apiClient.getAllFeatures().then(features => {
ReactDOM.render(
<FeatureProvider features={features}>
<App />
</FeatureProvider>,
document.getElementById('root'),
)
})
useFeature
Hook that is used to get a feature object from the context by its slug. Notice that it could be undefined
because the context only should contain the features that are enabled.
Specification
interface UseFeature<F extends DefaultFeature = DefaultFeature> {
(slug: string): F | undefined
}
Example
// src/app.tsx
import { useFeature } from 'toggled'
function App() {
const themeFF = useFeature('theme')
return (
<ThemeProvider theme={themeFF ? themeFF.settings.name : 'default'}>
<Component />
</ThemeProvider>
)
}
useFlagQueryFn
Hook that is used to get the magic function that can process a flag query.
Specification
interface UseFlagQueryFn {
(): (query: FlagQuery) => boolean
}
Example
import { useFlagQueryFn } from 'toggled'
export default function App() {
const flagQueryFn = useFlagQueryFn()
return (
<Layout designV2={flagQueryFn({ 'design-v2': true, 'design-v1': false })}>
{flagQueryFn('chat') && <ChatWidget>}
</Layout>
)
}
For more use cases, please go to the tests.
useFlag
Hook that is used to get a binary output based on the existence of a feature in the context. So, if the feature is in the context then the flag will be true
, otherwise false
.
The
useFlagQueryFn
hook is used internally.
Specification
interface UseFlag {
(query: FlagQuery): boolean
}
Example
import { useFlag } from 'toggled'
export default function App() {
const hasChat = useFlag('chat')
const hasDesignV2Only = useFlag({ 'design-v2': true, 'design-v1': false })
return (
<Layout designV2={hasDesignV2Only}>
{hasChat && <ChatWidget>}
</Layout>
)
}
<Flag />
Component to apply conditional rendering using a flagQuery
Specification
interface FlagProps {
flagQuery: FlagQuery
children: React.ReactNode
}
Example
import { Flag } from 'toggled'
export default function App() {
return (
<Flag flagQuery={{ 'design-v2': true, 'design-v1': false }}>
<Layout designV2={hasDesignV2Only}>
<Flag flagQuery="chat">
<ChatWidget>
</Flag>
</Layout>
</Flag>
)
}
<Feature />
Component to consume a feature object declaratively instead of useFeature
Specification
export interface FeatureProps {
slug: string
children(feature: DefaultFeature): React.ReactElement
}
Example
import { Feature } from 'toggled'
export default function App() {
return (
<Feature slug="chat">
{feature => {
return <ChatWidget settings={feature.settings}>
}}
</Feature>
)
}
License
MIT © Ricardo Q. Bazan