Skip to content
This repository was archived by the owner on Dec 7, 2022. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
{
"presets": [
"react",
"es2015",
"stage-0",
"flow"
]
[
"@babel/preset-env",
{
"targets": {
"node": "current"
}
}
],
"@babel/preset-react",
"@babel/preset-flow"
],
"plugins": ["@babel/plugin-syntax-export-default-from"]
}
47 changes: 27 additions & 20 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,14 @@
"react/prop-types": [0],

"eol-last": 2,
"indent": [2, 2, {"SwitchCase": 1, "VariableDeclarator": { "var": 2, "let": 2, "const": 3}}],
"indent": [
2,
2,
{
"SwitchCase": 1,
"VariableDeclarator": { "var": 2, "let": 2, "const": 3 }
}
],
"jsx-quotes": [2, "prefer-double"],
"linebreak-style": [2, "unix"],
"no-trailing-spaces": 1,
Expand All @@ -34,29 +41,29 @@
"react/display-name": 0,
"react/jsx-wrap-multilines": 2,

"flowtype/define-flow-type": 1
"flowtype/define-flow-type": 1,

"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",

"jest/no-disabled-tests": "warn",
"jest/no-focused-tests": "error",
"jest/no-identical-title": "error",
"jest/prefer-to-have-length": "warn",
"jest/valid-expect": "error"
},
"env": {
"es6": true,
"node": true,
"browser": true
},
"extends": [
"eslint:recommended",
"plugin:react/recommended",
],
"ecmaFeatures": {
"jsx": true,
"experimentalObjectRestSpread": true
"browser": true,
"jest/globals": true
},
"plugins": [
"react",
"flowtype"
],
"globals": {
"before": true,
"context": true,
"describe": true,
"it": true
"extends": ["eslint:recommended", "plugin:react/recommended"],
"plugins": ["jest", "flowtype", "react", "react-hooks"],
"settings": {
"react": {
"version": "detect",
"flowVersion": "0.93.0"
}
}
}
133 changes: 119 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,39 @@
# React-Launch-Darkly
*Simple component helpers to support LaunchDarkly in your react app.*

_Simple component helpers to support LaunchDarkly in your react app._

[![npm](https://img.shields.io/npm/v/react-launch-darkly.svg)](https://www.npmjs.com/package/react-launch-darkly)
[![Build Status](https://travis-ci.org/TrueCar/react-launch-darkly.svg?branch=master)](https://travis-ci.org/TrueCar/react-launch-darkly)

## Installation

`npm install --save react-launch-darkly`

## Dependencies
- React `v16.3.0` or greater
- If you use an older version of React, you can continue to use [version 1.4.0](https://github.com/TrueCar/react-launch-darkly/releases/tag/v1.4.0) of this library. However, we will no longer be actively maintaining version 1.x.
- [LaunchDarkly client](https://docs.launchdarkly.com/docs/js-sdk-reference) / `ldclient-js`
- `ldclient-js` needs to be a dependency within the app using `react-launch-darkly`
- supported versions of `ldclient-js`: `^1.1.12 || ^2.0.0`

- React `v16.8.0` or greater
- [LaunchDarkly client](https://docs.launchdarkly.com/docs/js-sdk-reference) / `launchdarkly-js-client-sdk`
- `launchdarkly-js-client-sdk` needs to be a dependency within the app using `react-launch-darkly`
- supported versions of `launchdarkly-js-client-sdk`: `^2.15.1`

## Basic Usage

To setup the `LaunchDarkly` component wrapper, you'll probably want to include it in a top-level
layout component:

```javascript
// MasterLayout.js
import React, { Component } from "react";
import { LaunchDarkly } from "react-launch-darkly";

export default class MasterLayout extends Component {
render () {
render() {
return (
<div>
<LaunchDarkly clientId={YOUR_LAUNCH_DARKLY_CLIENT_ID} user={{ key: "YOUR_USER_KEY" }}>
<LaunchDarkly
clientId={YOUR_LAUNCH_DARKLY_CLIENT_ID}
user={{ key: "YOUR_USER_KEY" }}
>
{this.props.children}
</LaunchDarkly>
</div>
Expand All @@ -36,13 +43,14 @@ export default class MasterLayout extends Component {
```

Then in your lower-level components, to make use of the `FeatureFlag` component:

```javascript
// Home.js
import React, { Component } from "react";
import { FeatureFlag } from "react-launch-darkly";

export default class Home extends Component {
render () {
render() {
return (
<div>
<FeatureFlag
Expand All @@ -53,15 +61,14 @@ export default class Home extends Component {
);
}

_renderFeature () {
return (
<div>Your new feature!</div>
);
_renderFeature() {
return <div>Your new feature!</div>;
}
}
```

## Docs

- [LaunchDarkly component](https://github.com/TrueCar/react-launch-darkly#launchdarkly-component)
- [FeatureFlag component](https://github.com/TrueCar/react-launch-darkly#featureflag-component)
- [SSR Support](https://github.com/TrueCar/react-launch-darkly#ssr-support)
Expand All @@ -71,36 +78,53 @@ export default class Home extends Component {
---

### `LaunchDarkly` component
Main component that initializes the [LaunchDarkly js-client](https://github.com/launchdarkly/js-client).

Main component that provides configuration data to `FeatureFlag` and `useFlags()`.

#### props

##### `clientId` : `string` (required)

This is the client id that is provided to you by LaunchDarkly.

##### `user` : `object` (required)

See the [LaunchDarkly docs](http://docs.launchdarkly.com/docs/js-sdk-reference#section-users) for more info.

##### `clientOptions` : `object` (optional)

Options that are passed to the LaunchDarkly JS client for additional configuration and features:

- [Bootstrapping](https://docs.launchdarkly.com/docs/js-sdk-reference#section-bootstrapping)
- [Secure Mode](https://docs.launchdarkly.com/docs/js-sdk-reference#section-secure-mode)
- [Setting LaunchDarkly Enterprise URLs](https://github.com/launchdarkly/js-client/blob/master/src/index.js#L241-L243)

##### `controlTest` : `(flagValue: FlagValueType) => boolean` (optional)

Control test function which is used by `useFlags` to determine whether control should be rendered. If none is provided, a default is used which checks if value starts with "control"

##### `challengerTest` : `(flagValue: FlagValueType) => boolean` (optional)

Challenger test function which is used by `useFlags` to determine whether challenger should be rendered. If none is provided, a default is used which checks if value does not start with "control"

---

### `FeatureFlag` component

Component that initializes the [LaunchDarkly js-client](https://github.com/launchdarkly/js-client).
Note that this component has to be rendered as a child of `LaunchDarkly`

#### props

##### `flagKey` : `string` (required)

The `flagKey` prop is the feature flag key you defined in LaunchDarkly.

##### `renderFeatureCallback` : `function` (required)

The main callback function that renders your feature. In typical scenarios where your flag is a boolean,
you can simply create your function to return the necessary JSX:

```javascript
// Example FeatureFlag component
<FeatureFlag flagKey="example" renderFeatureCallback={this._renderFeature} />
Expand All @@ -112,8 +136,81 @@ _renderFeature () {
```

##### Multivariate Flag Support

When using a multivariate feature flag, the `renderFeatureCallback` prop will pass the value of
the flag as an argument to your callback function:

```javascript
// Example FeatureFlag component
<FeatureFlag flagKey="multivariate-example" renderFeatureCallback={this._renderFeature} />

// Callback function with feature flag value passed in
_renderFeature (featureFlagValue) {
if (featureFlagValue === "A") {
return (<div>Bucket "A" Feature!</div>);
}

return (<div>Default Bucket Feature Here!</div>);
}
```

#### `initialRenderCallback` : `function` (optional)

Since the feature flags are requested from LaunchDarkly after DOM load, there may be some latency in the rendering. This render callback allows you to provide some sort of feedback to indicate loading, e.g., the typical spinning loader.

#### `renderDefaultCallback` : `function` (optional)

This callback is provided for cases where you want to render something by default, think of it when your feature flag is "off" or falsy.

---

### `useFlags` hook

Hook that initializes the [LaunchDarkly js-client](https://github.com/launchdarkly/js-client).
Note that any component using `useFlags` has to be rendered as a child of `LaunchDarkly`

#### arguments

##### `flagKey` : `string` (required)

The `flagKey` prop is the feature flag key you defined in LaunchDarkly.

#### return

##### `matchControl` : `() => boolean`

Helper which can be used to determine whether flag has a control value. `matchControl` internally uses `controlTest` to determine if flag has a control value.

##### `matchChallenger` : `() => boolean`

Helper which can be used to determine whether flag has a challenger value. `matchChallenger` internally uses `challengerTest` to determine if flag has a challenger value.

##### `match` : `(value: string) => boolean`

Helper which can be used to determine whether flag has a specific value.

```javascript
// Example usage
const TestComponent = (props = {}) => {
const { matchControl, matchChallenger, match } = useFlags("my-flag");
return (
<div>
{matchControl() && <p>Matched any control</p>}
{matchChallenger() && <p>Matched any challenger</p>}
{match("challenger2") && (
<p>Matched a specific challenger – challenger 2</p>
)}
<p>Matched nothing</p>
</div>
);
};
```

##### Multivariate Flag Support

When using a multivariate feature flag, the `renderFeatureCallback` prop will pass the value of
the flag as an argument to your callback function:

```javascript
// Example FeatureFlag component
<FeatureFlag flagKey="multivariate-example" renderFeatureCallback={this._renderFeature} />
Expand All @@ -129,16 +226,20 @@ _renderFeature (featureFlagValue) {
```

#### `initialRenderCallback` : `function` (optional)

Since the feature flags are requested from LaunchDarkly after DOM load, there may be some latency in the rendering. This render callback allows you to provide some sort of feedback to indicate loading, e.g., the typical spinning loader.

#### `renderDefaultCallback` : `function` (optional)

This callback is provided for cases where you want to render something by default, think of it when your feature flag is "off" or falsy.

---

### SSR Support

SSR is opt-in and you need to specify the initial set of feature flag keys and values through
the `bootstrap` property on `clientOptions`:

```javascript
// currentUser.featureFlags
// >> { "your-feature-flag": true }
Expand All @@ -154,9 +255,11 @@ feature flags within js-client's internal state. Thus taking precedence over the
present in `bootstrap`.

#### Disable LaunchDarkly js-client Initialization (Preventing XHRs)

In the event that you opt-in for SSR, you may not want to make any additional XHRs to LaunchDarkly
since you already have the feature flags provided from your server through `bootstrap`, you can
disable this by supplying `disableClient: true`:

```javascript
const clientOptions = {
bootstrap: currentUser.featureFlags,
Expand Down Expand Up @@ -213,6 +316,7 @@ http://localhost/users/101?features.show-user-email
```

#### Examples

```
// Overrides the `send-onboarding-email` boolean feature flag, setting it to `true`
http://localhost/users?features=send-onboarding-email
Expand All @@ -232,6 +336,7 @@ http://localhost/users/101?features.show-user-email
### Identify a new user

If you need to change the configured user for the launch darkly client you can do that by calling `identify`.

```
import { identify } from "react-launch-darkly";

Expand Down
Loading