Coder Social home page Coder Social logo

yusinto / ld-redux Goto Github PK

View Code? Open in Web Editor NEW
34.0 4.0 17.0 1.87 MB

A library to integrate launch darkly with react redux

License: MIT License

JavaScript 100.00%
launchdarkly launch-darkly launch darkly react redux integration feature flags toggle

ld-redux's Introduction

ld-redux

npm version npm downloads npm npm

A library to integrate launch darkly with react redux ๐Ÿ‘

Launch Darkly is a great tool for feature flagging and a/b testing. It has a fully capable client-side javascript sdk, so why this package?

If you use react redux and you want to store your feature flags as part of your redux state, this package will do that for you. It does the heavy lifting of:

  • Fetching your flags from launch darkly.
  • Storing it in your redux state.
  • Camel casing your keys so you can use them in code with the dot operator. The keys by default are dash separated so you can't do this out of the box with the official sdk.
  • Server Sent Event works as well so your app will respond live to feature flag changes without the users having to refresh the browser!

Breaking changes in v3.1

ld-redux v3.1.* is NOT backwards compatible! The init method now accepts dispatch instead of store. Follow the quickstart example below to see this.

Installation

yarn add ld-redux

Quickstart

  1. In your client bootstrap, initialise the launch darkly client by invoking the init method:

    import createStore from '<your-project>/store';
    import ldRedux from 'ld-redux';
    
    // standard redux createStore
    const store = createStore();
    
    // do this once
    ldRedux.init({
      clientSideId: 'your-client-side-id',
      dispatch: store.dispatch,
    });
    
    render(
      <Provider store={store}>
        <Router routes={routes} history={browserHistory}/>
      </Provider>,
      document.getElementById('reactDiv')
    );
  2. Include ldReducer as one of the reducers in your app:

    import { combineReducers } from 'redux';
    import ldRedux from 'ld-redux';
    import reducers from '<your-project>/reducers';
    
    export default combineReducers({
      ...reducers,
      LD: ldRedux.reducer(), // Note: the LD key can be anything you want
    });
  3. Use the flag:

    import React, {Component} from 'react';
    import {connect} from 'react-redux';
    
    const mapStateToProps = (state) => {
      const {featureFlagKey} = state.LD; // Note: the key LD must be the same as step 2.
    
      return {
        featureFlagKey,
      };
    };
    
    @connect(mapStateToProps)
    export default class Home extends Component {
      render() {
        return (
          <div>
            {
              /* look ma, feature flag! */
              this.props.featureFlagKey ?
                <div>
                  <p>Welcome to feature toggling!</p>
                </div>
                :
                'nothing'
            }
          </div>
        );
      }
    }

API

init({clientSideId, dispatch, flags, useCamelCaseFlagKeys, user, subscribe, options})

The init method accepts an object with the above properties. clientSideId, dispatch are mandatory.

The flags property is optional. This is an object containing all the flags you want to use and subscribe to in your app. If you don't specify this, ld-redux will subscribe to all flags in your ld environment.

// standard redux createStore
const store = createStore();
const flags = { 'feature-flag-key': false }; // only subscribe to  this one flag

// do this once
ldRedux.init({
  clientSideId: 'your-client-side-id',
  dispatch: store.dispatch,
  flags,
});

The subscribe property is optional. This defaults to true which means by default you'll get automatic live updates of flag changes from the server. You can turn this off and manually subscribe to flag changes through the ldClient object if for some reason you don't want to get live updates.

The user property is optional. You can initialise the sdk with a custom user by specifying one. This must be an object containing at least a "key" property. If you don't specify a user object, ldRedux will create a default one that looks like this:

const defaultUser = {
  key: uuid.v4(), // random guid
  ip: ip.address(),
  custom: {
    browser: userAgentParser.getResult().browser.name,
    device
  }
};

For more info on the user object, see here.

The useCamelCaseFlagKeys property is optional. This defaults to true which means by default the flags that are stored in redux will be camel cased. If this property is false, no transformation on the flag name will be done.

The options property is optional. It can be used to pass in extra options such as Bootstrapping. For example:

ldRedux.init({
    clientSideId,
    dispatch,
    flags,
    options: {
      bootstrap: 'localStorage',
    }
});

reducer()

This is ld-redux's reducer. You must include this reducer in your app as per step 2 above with any key of your choice. You then use this key to retrieve your flags from redux's state.

window.ldClient

Internally the ldRedux.init method above initialises the js sdk and stores the resultant ldClient object in window.ldClient. You can use this object to access the official sdk methods directly. For example, you can do things like:

// track goals
window.ldClient.track('add to cart');

// change user context
window.ldClient.identify({key: 'someUserId'});

For more info on changing user context, see the official documentation.

isLDReady

You no longer need to deal with isLDReady. However if you need to, it is still available in the store. You can access it via the LD state like so:

const mapStateToProps = (state) => {
  const {isLDReady} = state.LD; // Note: the key LD must be the same as step 2.

  return {
    isLDReady,
  };
};

This is useful to solve "flickering" issues above the fold on your front page caused by a flag transitioning from a default false value to true.

Example

Check the example for a fully working spa with react, redux and react-router. Remember to enter your client side sdk in the client bootstrap file before running the example!

ld-redux's People

Contributors

adamdicarlo avatar enceeobee avatar filipebarcos avatar gkadillak avatar thiskevinwang avatar willmcpo avatar yusinto avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

ld-redux's Issues

Call SET_FLAGS once for initialState

Hey @yusinto, just wondering if you've thought about only calling SET_FLAGS once for the initial state it receives from ld.

At the moment when we load our application we get a load of SET_FLAGS calls for each individual flag. I thought it might be beneficial to just have an INITIAL_SET_ALL_FLAGS that gets called once on initial load

Not able to see any values in Redux store

I have followed the steps and i could see that LD as an empty object getting created ib redux store . but do not see any value coming back from LD. also at the same time i do not see any error message in console . is there any way i can validate what is wrong ?

init with dispatch instead of store

I have a suggestion that would be an easy change, but also a breaking change that I believe would make the init method more flexible on where it can be used. I can make the PR, but wanted to start a discussion on it first.

Problem: ldRedux.init expects the redux store as the second arg. This works great if you are initiating ldRedux close to where you are initiating the redux store. However, in practice, the ldRedux is more likely initiated in close proximity to where the user is created/authenticated, when the data is available. In our case, that handler is a thunk, which does not have access to store directly.

Solution: Based on the code in https://github.com/yusinto/ld-redux/blob/master/src/init.js, the only usage of store is to call store.dispatch. store by itself is not something that is commonly (or easily) passed around. However, dispatch is meant to be passed around in both connected components and middleware such as redux-thunk. So as an easy way to make init more flexible in where it can be used, the proposed solution is to replace store with dispatch as the second argument of ldRedux.init and call it directly within the underlying init methods

Custom key option in reducer

In the existing approach, in reducer, we receive a camelCased key based on our feature flag key. If we have a long key name this-is-a-long-long-long-key-name, it gives a key thisIsALongLongLongKeyName which violates eslint id-length.
It would be a good feature, if we provide custom key instead of camelCased key.
Proposal approach:

const flags = [
  {
    key: 'feature-flag-key',
    default: false,
    reduxKey: 'myCustomKey',
  },
];

In the reducer, we should receive a LD object with our custom key.

{
  LD: {
   isLDReady: true,
   myCustomKey: false
  }
}

[FEATURE REQUEST] Allow camel casing to be turned off

Hello! First of all, loving the library. It fills a real need that we've had in our codebase and the source for it is dead simple. However, when refactoring code to use this library, renaming each flag from dashes to camel case was burdensome and error-prone. It would be great if there was a way to keep the flags as dash-cased on init. One idea I had was to add another option in the init method:

ldRedux.init({
  clientSideId: 'your-client-side-id',
  camelCase: false,
  dispatch: store.dispatch,
  flags,
});

I'd be more than happy to put up a pr for this feature myself. What are your thoughts on this approach?

Upgrade to new LD client package names

Hi!

I love this library. I've been using it for like three years and it's the best.

Can you upgrade to the new LD client libraries, launchdarkly-js-client-sdk and the like?

Package managers won't upgrade past 2.10.2 because the package name has changed. Details are here: https://github.com/launchdarkly/js-client-sdk/blob/master/CHANGELOG.md#2103---2019-05-08

I would be open to doing some of the work if you have an idea of how best to do this. Would it be a new major? I don't think the ld-redux API would need to change much, so perhaps it can be a minor?

Thank you!

`subscribeToChanges` always uses camelCase

Hello ๐Ÿ‘‹ ,

First, thank you for this package! It's wonderful ๐Ÿ˜„

Issue

I recently noticed that subscribeToChanges always uses a camelCase flag key when updating the store, even when useCamelCaseFlagKeys is set to false.

I don't think the fix should be too difficult, and I can probably supply a PR for it soon, but I wanted to open an issue for posterity.

Steps to reproduce

  1. init ld-redux setting useCamelCaseFlagKeys to false

    ldRedux.init({
      clientSideId: 'your-client-side-id',
      dispatch: store.dispatch,
      useCamelCaseFlagKeys: false
    });
  2. Later, identify a new user, which will dispatch an event(s) to update the store once the new flag values are ready

    window.ldClient.identify({key: 'someUserId'});
  3. Observe that the flag key in the SET_FLAG action is incorrectly sent in camelCase (e.g. 'my-kebab-case-flag' is updated as 'myKebabCaseFlag')

Screenshots

Here's an example of a flag being updated in camelCase, when it should be kebab-case

camelCase

Deprecation warning

Thanks @yusinto for such a great library. I'm getting below minor warning in console. Looks like it's coming from main library ldclient-js. May be we can try bumping up version. Not sure whether they've fixed it.

[LaunchDarkly] "default export" is deprecated, please use "named LDClient export"

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.