NAV
  • React/React Native (Client-Side) SDK
  • React/React Native (Client-Side) SDK

    Introduction

    Welcome to the developer documentation for the Kameleoon React SDK! Our SDK gives you the possibility of running experiments and activating feature flags on your front-end React/React Native application. Integrating our SDK into your web-application is easy, and its footprint (in terms of memory and network usage) is low.

    React SDK provides functionality that can be used for both React and React Native. Most of the methods work the exact same way, however React Native uses different way of storing feature flag related data - AsyncStorage instead of browser localStorage, taking in consideration that all the calls to AsyncStorage has to be asynchronous, some methods has to be handled in a different way.

    Requirements

    React SDK requires React 16.8.0+

    Installation

    npm install @kameleoon/react-sdk
    yarn add @kameleoon/react-sdk
    

    Installing the React SDK can be directly achieved through an npm or yarn module:

    Contents

    Follow the Usage section to configure your React App for work with feature flags. This section contains all the main steps for proper Kameleoon React SDK configuration as well as main tools for development.

    Optionally discover Additional Tools section containing useful information on a number of features, some useful utility hooks and higher-order components which will simplify the usage of React JS SDK.

    Finally Technical Considerations section presents some technical insights on how SDK operates.

    Usage

    Here is a step by step guide for configuring React JS SDK for your application.

    1. Create and configure Kameleoon Client

    For the start developers will need to create an entry point for React SDK by creating Kameleoon Client on the top level of your application which is done by using createClient() function imported from kameleoon package.

    createClient

    import { createClient } from "@kameleoon/react-sdk";
    
    const client = createClient({
      siteCode: "0fpmcg34lg",
      visitorCode: "280295",
      options: {
        visitor_data_maximum_size: 1,
        actions_configuration_refresh_interval: 60,
        environment: 'staging'
      },
    });
    

    A KameleoonClient is created using createClient() callback function to run experiments and retrieve the status of feature flag and its variables.

    Arguments
    Returns
    Types

    2. Wrap application in Kameleoon Provider

    The second step is connecting previously created Kameleoon Client to Kameleoon Provider. It can be done by passing configured client to Kameleoon Provider as follows.

    KameleoonProvider

    import { KameleoonProvider, createClient } from '@kameleoon/react-sdk';
    
    const client = createClient({
      siteCode: '0fpmcg34lg'
      visitorCode: '280295',
      options: {
        visitor_data_maximum_size: 1,
        actions_configuration_refresh_interval: 60,
        environment: 'staging'
      }
    });
    
    function AppWrapper(): JSX.Element {
      return (
        <KameleoonProvider client={client}>
          <App />
        </KameleoonProvider>
      )
    }
    

    Use this provider on root level by wrapping your app to gain an access to KameleoonClient. This ensures your app does not flicker due to flag changes at startup time.

    Props
    Types

    3. Get access to feature flag inside React Components.

    Now that Kameleoon Client is created, configured and added to a Kameleoon Provider it is possible to communicate with your feature flag from the inside of React Components.

    There are two main ways in React SDK API that allow developer to grant control over the most important feature flag information useActivateFeature and useFeature hooks.

    Further is the detailed description on the usage of hooks and higher order component for gaining access to useActivateFeature and useFeature.

    useActivateFeature

    import { useEffect } from 'react';
    import { useActivateFeature, useVisitorCode, KameleoonException } from '@kameleoon/react-sdk';
    
    function MyComponent(): JSX.Element {
      const { hasFeature, error } = useActivateFeature();
      const { getVisitorCode } = useVisitorCode();
    
      useEffect(() => {
        const visitorCode = getVisitorCode('example.com');
        const featureKey = 'example-feature-key';
    
        if (error?.type === KameleoonException.NotTargeted) {
          // Handle error
        }
    
        if (error?.type === KameleoonException.FeatureConfigurationNotFound) {
          // Handle error
        }
    
        const feature = hasFeature(featureKey, visitorCode);
      }, []);
    
      ...
    }
    

    A hook that returns a callback function hasFeature() which validates if user has been associated with this feature along with an error object. If the callback fails, it returns value false, otherwise true.

    If feature flag is not activated, KameleoonException.FeatureConfigurationNotFound exception will be thrown.

    Callback arguments
    Callback returns
    Exceptions Thrown
    Error Handling

    An error object returned from the hook contains message: string, name: string and type: KameleoonException fields. A type key may be useful for comparing the returned error type against KameleoonException enum. If there were no errors error will be null.

    Types

    useFeature

    import { Button } from "@kameleoon/ui";
    import { useFeature, useVisitorCode, KameleoonException } from "@kameleoon/react-sdk";
    
    function MyComponent(): JSX.Element {
      const { getVisitorCode } = useVisitorCode();
      const { feature, errors } = useFeature({
        featureKey: "red-button",
        variableKeys: { production: 'red-button' },
        visitorCode: getVisitorCode("example.com"),
      });
    
      for (const error of errors) {
        if (error.type === KameleoonException.NotTargeted) {
          // Handle error
        }
    
        if (error.type === KameleoonException.FeatureConfigurationNotFound) {
          // Handle error
        }
      }
    
      const { isActive, variables } = feature;
    
      return <Button theme={isActive ? "red" : "green"} />;
    }
    

    A hook that returns feature object containing the information on both feature flag status and the list of variables along with an errors object, containing a list of kameleoon client errors. If not provided with visitorCode, will automatically generate one.

    Arguments
    Returns

    An object featureResult containing feature flag information and errors array of kameleoon client errors.

    In the examples, if the feature flag with the red-button key is enabled, theme of button will set to red.

    Exceptions Thrown
    Error Handling

    An errors array returned from the hook contains a list of KameleoonError. Each errors' type key may be useful for comparing the returned error type against KameleoonException enum. If there were no errors errors array will be empty.

    Types

    withActivateFeature

    import { withVisitorCode, withActivateFeature, compose, KameleoonException } from '@kameleoon/react-sdk';
    
    class MyComponent extends React.Component {
      componentDidMount() {
        const { getVisitorCode, hasFeature, activateFeatureError } = this.props;
        const visitorCode = getVisitorCode('example.com');
        const featureKey = 'example-feature-key';
    
        if (activateFeatureError?.type === KameleoonException.NotTargeted) {
          // Handle error
        }
    
        if (activateFeatureError?.type === KameleoonException.FeatureConfigurationNotFound) {
          // Handle error
        }
    
        const feature = hasFeature(featureKey, visitorCode);
      }
    
      ...
    }
    
    export default compose(withVisitorCode, withActivateFeature)(MyComponent);
    

    An alternative of useActivateFeature for React Class Components.

    Returns
    Error Handling

    Unlike useActivateFeature hook, which retrieves and error object, withActivateFeature HOC enhances props with named activateFeatureError error to avoid error naming intersection when using several HOCs within compose.

    withFeature

    import { Button } from "@kameleoon/ui";
    import { withFeature, KameleoonException } from "@kameleoon/react-sdk";
    
    class MyComponent extends React.Component {
      state = { 
        isActive: false,
        variables: [],
      }
    
      componentDidMount() {
        const { feature, errors } = this.props;
    
        for (const error of errors) {
          if (error.type === KameleoonException.NotTargeted) {
            // Handle error
          }
    
          if (error.type === KameleoonException.FeatureConfigurationNotFound) {
            // Handle error
          }
        }
    
        const { isActive, variables } = feature;
    
        this.setState({ isActive, variables });
      }
    
      render() {
        return <Button theme={isActive ? "red" : "green"} />;
      }
    }
    
    export default withFeature({
      featureKey: "red-button",
      variableKeys: { production: 'red-button' },
      visitorCode: "280295",
    })(MyComponent);
    

    An alternative for useFeature for React Class Components.

    Arguments
    Returns

    In the examples, if the feature flag with the red-button key is enabled, theme of button will set to red.

    Feature

    import { Button } from '@kameleoon/ui';
    import { Feature } from '@kameleoon/react-sdk';
    
    class MyComponent extends React.Component {
      state = { 
        isActive: false,
        variables: [],
      }
    
      componentDidMount() {
        const { feature, errors } = this.props;
    
        for (const error of errors) {
          if (error.type === KameleoonException.NotTargeted) {
            // Handle error
          }
    
          if (error.type === KameleoonException.FeatureConfigurationNotFound) {
            // Handle error
          }
        }
    
        const { isActive, variables } = feature;
    
        this.setState({ isActive, variables });
      }
    
      render() {
        return <Button theme={this.isActive ? 'red' : 'green'} />;
      }
    }
    
    class MyComponentWrapper extends React.Component { 
      render() {
        return(
          <Feature 
            featureKey="red-button" 
            variableKeys={{production: "red-button"}} 
            visitorCode="280295"
          >
            (({ feature, errors }) => (
              <MyComponent feature={feature} errors={errors} />
            ))
          </Feature>;
        )
      }
    }
    

    An alternative for useFeature and withFeature which utilizes React Render Props pattern.

    Props
    Returns

    A wrapped component with the following props:

    In the examples, if the feature flag with the red-button key is enabled, theme of button will set to red.

    4. (Optional) Use Kameleoon Client directly.

    React JS SDK allows the developer to communicate with Kameleoon JS/TS sdk directly ignoring existing hooks and HOCs methods.

    useKameleoon

    import { useKameleoon } from '@kameleoon/react-sdk';
    
    function MyComponent(): JSX.Element {
      const { client } = useKameleoon();
    
      ...
    }
    

    Gives direct access to Kameleoon JS/TS client.

    Returns

    withKameleoon

    import { withKameleoon } from '@kameleoon/react-sdk';
    
    class MyComponent extends React.Component {
      componentDidMount() {
        const { client } = this.props;
      }
    
      ...
    }
    
    export default withKameleoon(MyComponent);
    

    An alternative to useKameleoon, gives direct access to Kameleoon JS/TS client.

    Returns

    Additional Tools

    Variation Associated Data

    A callback function getVariationAssociatedData() retrieved via useVariationAssociatedData or withVariationAssociatedData obtains JSON data associated with a variation. The JSON data usually represents some metadata of the variation, and can be configured on our web application interface or via our Automation API.

    useVariationAssociatedData

    import { useEffect } from 'react';
    import { useVariationAssociatedData, KameleoonException } from '@kameleoon/react-sdk';
    
    function MyComponent(): JSX.Element {
      const { getVariationAssociatedData } = useVariationAssociatedData();
    
      useEffect(() => {
        const variationId = 280295;
    
        if (error?.type === KameleoonException.VariationConfigurationNotFound) {
          // Handle error
        }
    
        const data = getVariationAssociatedData(variationId);
      }, []);
    
      ...
    }
    

    Callback function getVariationAssociatedData takes the variationId as a parameter and will return the data as a JavaScript object along with an error object. It will throw an exception (KameleoonException.VariationConfigurationNotFound) if the variationId is wrong or corresponds to an experiment that is not yet online.

    Callback Arguments
    Callback Returns
    Exceptions Thrown
    Error Handling

    An error object returned from the hook contains message: string, name: string and type: KameleoonException fields. A type key may be useful for comparing the returned error type against KameleoonException enum. If there were no errors error will be null.

    Types

    withVariationAssociatedData

    import { withVariationAssociatedData, KameleoonException } from '@kameleoon/react-sdk';
    
    class MyComponent extends React.Component {
      componentDidMount() {
        const { getVariationAssociatedData, variationAssociatedDataError } = this.props;
        const variationId = 280295;
    
        if (variationAssociatedDataError?.type === KameleoonException.VariationConfigurationNotFound) {
          // Handle error
        }
    
        const data = getVariationAssociatedData(variationId);
      }
    
      ...
    }
    
    export default withVariationAssociatedData(MyComponent);
    

    An alternative for useVariationAssociatedData.

    Arguments
    Returns
    Error Handling

    Unlike useVariationAssociatedData hook, which retrieves and error object, withVariationAssociatedData HOC enhances props with named variationAssociatedDataError error to avoid error naming intersection when using several HOCs within compose.

    Local Storage Key

    import { KAMELEOON_SDK_LOCAL_STORAGE_KEY } from "@kameleoon/react-sdk";
    

    KAMELEOON_SDK_LOCAL_STORAGE_KEY - is a constant which used as a key in LocalStorage to store a kameleoonTargetingData and kameleoonConfiguration.

    Visitor Code

    A callback function getVisitorCode() retrieved by useVisitorCode or withVisitorCode obtains the Kameleoon visitorCode for the current visitor. This is especially important when using Kameleoon in a mixed front-end and back-end environment, where user identification consistency must be guaranteed. The implementation logic:

    1. First we check if a kameleoonVisitorCode cookie can be found. If so, we will use this as the visitor identifier.

    2. If no cookie was found, we either randomly generate a new identifier, or use the defaultVisitorCode argument as identifier if it is passed. This allows our customers to use their own identifiers as visitor codes, should they wish to. This can have the added benefit of matching Kameleoon visitors with their own users without any additional look-ups in a matching table.

    3. In any case, the JavaScript kameleoonVisitorCode cookie is set with the value. Then this identifier value is finally returned by the method.

    For more information, refer to this article.

    useVisitorCode

    import { useEffect } from 'react';
    import { uuidv4 } from 'uuid';
    import { useVisitorCode } from '@kameleoon/react-sdk';
    
    function MyComponent(): JSX.Element {
      const { getVisitorCode } = useVisitorCode();
    
      useEffect(() => {
        // Without defaultVisitorCode argument
        const visitorCode = getVisitorCode('example.com');
    
        // With defaultVisitorCode argument
        const visitorCode = getVisitorCode('example.com', uuidv4());
      }, []);
    
      ...
    }
    

    useVisitorCode returns a callback function getVisitorCode() used to obtain visitor code.

    Callback Arguments
    Callback Returns

    withVisitorCode

    import { uuidv4 } from 'uuid';
    import { withVisitorCode } from '@kameleoon/react-sdk';
    
    class MyComponent extends React.Component {
      componentDidMount() {
        const { getVisitorCode } = this.props;
    
        // Without defaultVisitorCode argument
        const visitorCode = getVisitorCode('example.com');
    
        // With defaultVisitorCode argument
        const visitorCode = getVisitorCode('example.com', uuidv4());
      }
    
      ...
    }
    
    export default withVisitorCode(MyComponent);
    

    An alternative for useVisitorCode hook.

    Arguments
    Returns

    Trigger an Experiment

    A callback function getVariationId() retrieved via useTriggerExperiment or useTriggerExperiment takes visitorCode and experimentId as mandatory arguments to register a variation for a given user.

    If such a user has never been associated with any variation, the SDK returns a randomly selected variation. If a user with a given visitorCode is already registered with a variation, it will detect the previously registered variation and return the variationId.

    You have to make sure that proper error handling is set up in your code as shown in the example to the right to catch potential exceptions.

    Please Note:

    useTriggerExperiment

    import { useEffect } from 'react';
    import { useTriggerExperiment, useVisitorCode, KameleoonException } from '@kameleoon/react-sdk';
    
    function MyComponent(): JSX.Element {
      const { getVariationId, error } = useTriggerExperiment();
      const { getVisitorCode } = useVisitorCode();
    
      useEffect(() => {
        const visitorCode = getVisitorCode('example.com');
        const experimentId = 12341;
    
        if (error?.type === KameleoonException.ExperimentConfigurationNotFound) {
          // Handle exception
        }
    
        if (error?.type === KameleoonException.NotTargeted) {
          // Handle exception
        }
    
        if (error?.type === KameleoonException.NotActivated) {
          // Handle exception
        }
    
        const variationId = getVariationId(visitorCode, experimentId);
      }, []);
    
      ...
    }
    

    useTriggerExperiment hook returns getVisitorCode() function used for triggering an experiment along with an error object.

    Callback Arguments
    Callback Returns
    Exceptions Thrown
    Error Handling

    An error object returned from the hook contains message: string, name: string and type: KameleoonException fields. A type key may be useful for comparing the returned error type against KameleoonException enum. If there were no errors error will be null.

    Types

    withTriggerExperiment

    import { withVisitorCode, withTriggerExperiment, compose, KameleoonException } from '@kameleoon/react-sdk';
    
    class MyComponent extends React.Component {
      componentDidMount() {
        const { getVisitorCode, getVariationId, triggerExperimentError } = this.props;
        const visitorCode = getVisitorCode('example.com');
        const experimentId = 230243;
    
        if (triggerExperimentError?.type === KameleoonException.ExperimentConfigurationNotFound) {
          // Handle exception
        }
    
        if (triggerExperimentError?.type === KameleoonException.NotTargeted) {
          // Handle exception
        }
    
        if (triggerExperimentError?.type === KameleoonException.NotActivated) {
          // Handle exception
        }
    
        const variationId = getVariationId(visitorCode, experimentId);
      }
    
      ...
    }
    
    export default compose(withVisitorCode, withTriggerExperiment)(MyComponent);
    

    An alternative for useTriggerExperiment hook.

    Arguments
    Returns
    Error Handling

    Unlike useTriggerExperiment hook, which retrieves and error object, withTriggerExperiment HOC enhances props with named triggerExperimentError error to avoid error naming intersection when using several HOCs within compose.

    Feature Variables

    A callback function getFeatureVariable() retrieved by useFeatureVariable or withFeatureVariable is used to obtain a feature variable.

    This callback function takes two input parameters: featureKey and variableKey. It will return the data with the expected type, as defined on the web interface. It will throw an exception (KameleoonException.FeatureConfigurationNotFound) if the requested feature has not been found in the internal configuration of the SDK.

    useFeatureVariable

    import { useEffect } from 'react';
    import { useFeatureVariable, KameleoonException } from '@kameleoon/react-sdk';
    
    function MyComponent(): JSX.Element {
      const { getFeatureVariable, error } = useFeatureVariable();
    
      useEffect(() => {
        const featureKey = 'example-feature-key';
        const variableKey = 'example-variable-key';
    
        if (error?.type === KameleoonException.FeatureConfigurationNotFound) {
          // Handle error
        }
    
        const featureVariable = getFeatureVariable(featureKey, variableKey);
      }, []);
    
      ...
    }
    

    useFeatureVariable returns a callback function getFeatureVariable() used to obtain feature variables along with an error object.

    Callback Arguments
    Callback Returns
    Exceptions Thrown
    Error Handling

    An error object returned from the hook contains message: string, name: string and type: KameleoonException fields. A type key may be useful for comparing the returned error type against KameleoonException enum. If there were no errors error will be null.

    Types

    withFeatureVariable

    import { withFeatureVariable, KameleoonException } from '@kameleoon/react-sdk';
    
    class MyComponent extends React.Component {
      componentDidMount() {
        const { getFeatureVariable, featureVariableError } = this.props;
        const featureKey = 'example-feature-key';
        const variableKey = 'example-variable-key';
    
        if (featureVariableError?.type === KameleoonException.FeatureConfigurationNotFound) {
          // Handle error
        }
    
        const featureVariable = getFeatureVariable(featureKey, variableKey);
      }
    
      ...
    }
    
    export default withFeatureVariable(MyComponent);
    

    An alternative for useFeatureVariable hook.

    Arguments
    Returns
    Error Handling

    Unlike useFeatureVariable hook, which retrieves and error object, withFeatureVariable HOC enhances props with named featureVariableError error to avoid error naming intersection when using several HOCs within compose.

    Track Conversion

    A callback function trackConversion() retrieved via useTrackingConversion or withTrackingConversion is used to track conversion. This callback function requires visitorCode and goalId to track conversion on this particular goal. In addition, trackConversion() accepts third optional argument revenue to track revenue. The visitorCode is usually identical to the one that was used when triggering the experiment.

    trackConversion() doesn't return any value. And it is non-blocking as the server call is made asynchronously.

    useTrackingConversion

    import { useEffect } from 'react';
    import { useTrackingConversion, useVisitorCode } from '@kameleoon/react-sdk';
    
    function MyComponent(): JSX.Element {
      const { getVisitorCode } = useVisitorCode();
      const { trackConversion } = useTrackingConversion();
    
      useEffect(() => {
        const visitorCode = getVisitorCode('example.com')
        const goalId = 280295;
    
        trackConversion(visitorCode, goalId);
      }, []);
    
      ...
    }
    

    useTrackingConversion returns a callback function trackConversion() used for tracking conversion.

    Callback Arguments
    Returns

    withTrackingConversion

    import { withVisitorCode, withTrackingConversion, compose } from '@kameleoon/react-sdk';
    
    class MyComponent extends React.Component {
      componentDidMount() {
        const { getVisitorCode, trackConversion } = this.props;
        const visitorCode = getVisitorCode('example.com');
        const goalId = 230234;
    
        trackConversion(visitorCode, goalId);
      }
    
      ...
    }
    
    export default compose(withVisitorCode, withTrackingConversion)(MyComponent);
    

    An alternative for useTrackingConversion hook.

    Arguments
    Returns

    Retrieve Data from Remote Source

    An callback function retrieveDataFromRemoteSource() retrieved via useRetrieveDataFromRemoteSource or withRetrieveDataFromRemoteSource can be used to retrieve data using specific key and siteCode from Kameleoon provider. The Data is stored on a remote Kameleoon server. Usually data will be stored on our remote servers via the use of our Data API. This method, along with the availability of our highly scalable servers for this purpose, provides a convenient way to quickly store massive amounts of data that can be later retrieved for each of your visitors / users.

    useRetrieveDataFromRemoteSource

    import { useEffect, useCallback } from 'react';
    import { useRetrieveDataFromRemoteSource, RemoteSourceDataType } from '@kameleoon/react-sdk';
    
    function MyComponent(): JSX.Element {
      const { retrieveDataFromRemoteSource } = useRetrieveDataFromRemoteSource();
    
      const processRetrievedData = useCallback(async () => {
        const data: RemoteSourceDataType = await retrieveDataFromRemoteSource('example-key');
        // Your code
      }, [retrieveDataFromRemoteSource]);
    
      useEffect(() => {
        processRetrievedData();
      }, [processRetrievedData]);
    
      ...
    }
    

    useRetrieveDataFromRemoteSource returns retrieveDataFromRemoteSource() callback used for obtaining remote data.

    Callback Arguments
    Callback Returns
    Returns
    Types

    withRetrieveDataFromRemoteSource

    import { withRetrieveDataFromRemoteSource, RemoteSourceDataType } from '@kameleoon/react-sdk';
    
    class MyComponent extends React.Component {
      async componentDidMount() {
        const { retrieveDataFromRemoteSource } = this.props;
        const data: RemoteSourceDataType = await getVariationAssociatedData('example-key');
        // Your code
      }
    
      ...
    }
    
    export default withRetrieveDataFromRemoteSource(MyComponent);
    

    An alternative for useRetrieveDataFromRemoteSource hook.

    Arguments
    Returns
    Types

    Add User Data

    A callback function addData() retrieved via useAddData or withAddData is used to add various data to associate with the current user. This callback function requires visitorCode as a first parameter and then accepts array of various Data Types allowed in Kameleoon.

    useAddData

    import { useEffect } from 'react';
    import { Button } from '@kameleoon/ui';
    import {
      useAddData,
      useBrowser,
      useCustomData,
      useVisitorCode,
      Browser
    } from '@kameleoon/react-sdk';
    
    function MyComponent(): JSX.Element {
      const { getVisitorCode } = useVisitorCode();
      const { addData } = useAddData();
      const { addBrowser } = useBrowser();
      const { addCustomData } = useCustomData();
    
      useEffect(() => {
        const visitorCode = getVisitorCode('example.com');
    
        // Single data type passed
        addData(visitorCode, addBrowser(Browser.Chrome));
    
        // Several data types passed
        addData(visitorCode, addBrowser(Browser.Chrome), addCustomData(1, 'some custom value'));
      }, []);
    
      ...
    }
    

    useAddData hook returns a callback function addData used to add client associated data. useAddData, useBrowser and useCustomData shown in code example are used to correctly structure the data.

    Callback Arguments

    withAddData

    import {
      withAddData,
      withBrowser,
      withCustomData,
      withVisitorCode,
      Browser,
      compose,
    } from '@kameleoon/react-sdk';
    
    class MyComponent extends React.Component {
      componentDidMount() {
        const { addData, addBrowser, addCustomData, getVisitorCode } = this.props;
        const visitorCode = getVisitorCode('example.com');
    
        // Single data type passed
        addData(visitorCode, addBrowser(Browser.Chrome));
    
        // Several data types passed
        addData(visitorCode, addBrowser(Browser.Chrome), addCustomData(1, 'some custom value'));
      }
    
      ...
    }
    
    export default compose(
      withAddData,
      withBrowser,
      withCustomData,
      withVisitorCode,
    )(MyComponent);
    

    An alternative for useAddData hook.

    Arguments
    Returns

    Run When Ready

    In certain scenarios when working with Kameleoon API it's important to make sure that the client was initialized properly within the certain timeout. It's especially important while using triggerExperiment() or trackConversion(). For these cases it's possible to use runWhenReady() function, retrieved by useRunWhenReady hook or withRunWhenReady high-order component.

    useRunWhenReady

    import { useEffect, useCallback, useState } from 'react';
    import { useRunWhenReady, useTriggerExperiment } from '@kameleoon/react-sdk';
    
    function MyComponent(): JSX.Element {
      const { runWhenReady } = useRunWhenReady();
      const { getVariationId } = useTriggerExperiment();
    
      const [variationId, setVariationId] = useState<number>(0);
    
      const getVariationSuccessCallback = useCallback(() => {
        const id = getVariationId('user_id', 12345);
        setVariationId(id);
      }, [getVariationId, isRenderProps]);
    
      const getVariationErrorCallback = useCallback(() => {
        throw new Error(
          "Couldn't get server configuration from HTTP request in a specified time",
        );
      }, []);
    
      useEffect(() => {
        runWhenReady(
          getVariationSuccessCallback,
          getVariationErrorCallback,
          1000,
        );
      }, [runWhenReady, getVariationSuccessCallback, getVariationErrorCallback]);
    
      ...
    }
    

    The runWhenReady() function makes sure that Kameleoon Client will be initialized properly using HTTP call withing the specified timeout.

    Callback Arguments

    withRunWhenReady

    import { useEffect, useCallback, useState } from 'react';
    import { withRunWhenReady, withTriggerExperiment } from '@kameleoon/react-sdk';
    
    class MyComponent extends React.Component {
      variationSuccessCallback(): void {
        const id = this.props.getVariationId('user_id', 12345);
        setVariationId(id);
      }
    
      variationErrorCallback(): void {
        const id = this.props.getVariationId('user_id', 12345);
        setVariationId(id);
      }
    
      componentDidMount() {
        this.props.runWhenReady(this.variationSuccessCallback, this.variationErrorCallback, 1000);
      }
    
      ...
    }
    
    export default compose(
      withRunWhenReady,
      withTriggerExperiment,
    )(MyComponent);
    

    An alternative for useRunWhenReady hook.

    Arguments
    Returns

    Data Flush

    Data associated with the current user via addData() is not immediately sent to the server. It is stored and accumulated until it is sent automatically by the triggerExperiment() or trackConversion(), or manually by the flush() callback function which is retrieve via useFlush or withFlush. This allows the developer to control exactly when the data is flushed to servers. For instance, if you call the addData() a dozen times, it would be a waste of resources to send data to the server after each addData() invocation. Just call flush() once at the end.

    The flush() callback function is non-blocking as the server call is made asynchronously.

    useFlush

    import { useEffect } from 'react';
    import { Button } from '@kameleoon/ui';
    import {
      useAddData,
      useBrowser,
      useVisitorCode,
      useFlush,
      Browser
    } from '@kameleoon/react-sdk';
    
    function MyComponent(): JSX.Element {
      const { getVisitorCode } = useVisitorCode();
      const { addData } = useAddData();
      const { addBrowser } = useBrowser();
      const { flush } = useFlush();
    
      useEffect(() => {
        const visitorCode = getVisitorCode('example.com');
    
        addData(visitorCode, addBrowser(Browser.Chrome));
        flush(visitorCode);
      }, []);
    
      ...
    }
    

    useFlush return a callback function flush(). useBrowser used in the example is used to structure the data correctly.

    Callback Arguments

    withFlush

    import {
      withVisitorCode,
      withAddData,
      withBrowser,
      withFlush,
      Browser,
      compose,
    } from '@kameleoon/react-sdk';
    
    class MyComponent extends React.Component {
      componentDidMount() {
        const { addData, addBrowser, flush, getVisitorCode } = this.props;
        const visitorCode = getVisitorCode('example.com');
    
        addData(visitorCode, addBrowser(Browser.Chrome));
        flush(visitorCode);
      }
    
      ...
    }
    
    export default compose(
      withAddData,
      withBrowser,
      withFlush,
      withVisitorCode,
    )(MyComponent);
    

    An alternative for useFlush hook.

    Arguments
    Returns

    Data Structure

    The following set of hooks and higher-order components are serving the purpose of structuring the data passed to addData() function correctly.

    useBrowser

    import { useEffect } from 'react';
    import { useAddData, useBrowser, useVisitorCode, Browser } from '@kameleoon/react-sdk';
    
    function MyComponent(): JSX.Element {
      const { addData } = useAddData();
      const { getVisitorCode } = useVisitorCode();
      const { addBrowser } = useBrowser();
    
      useEffect(() => {
        const visitorCode = getVisitorCode('example.com');
    
        addData(visitorCode, addBrowser(Browser.Chrome));
      }, []);
    
      ...
    }
    

    useBrowser return a callback function addBrowser() which add chosen browser to user associated data.

    Callback Arguments
    Callback Returns
    Types

    withBrowser

    import {
      withVisitorCode,
      withAddData,
      withBrowser,
      Browser,
      compose,
    } from '@kameleoon/react-sdk';
    
    class MyComponent extends React.Component {
      componentDidMount() {
        const { addData, addBrowser, getVisitorCode } = this.props;
        const visitorCode = getVisitorCode('example.com');
    
        addData(visitorCode, addBrowser(Browser.Chrome));
      }
    
      ...
    }
    
    export default compose(
      withAddData,
      withBrowser,
      withVisitorCode,
    )(MyComponent);
    

    An alternative for useBrowser hook.

    Arguments
    Returns

    usePageView

    import { useEffect } from 'react';
    import { useAddData, usePageView, useVisitorCode } from '@kameleoon/react-sdk';
    
    function MyComponent(): JSX.Element {
      const { addData } = useAddData();
      const { getVisitorCode } = useVisitorCode();
      const { addPageView } = usePageView();
    
      useEffect(() => {
        const visitorCode = getVisitorCode('example.com');
    
        addData(visitorCode, addPageView('example.com', 'title', 3));
      }, []);
    
      ...
    }
    

    usePageView hook returns a callback function addPageView() used to structure page view data.

    Callback Arguments
    Callback Returns
    Types

    withPageView

    import {
      withVisitorCode,
      withAddData,
      withPageView,
      compose,
    } from '@kameleoon/react-sdk';
    
    class MyComponent extends React.Component {
      componentDidMount() {
        const { addData, addPageView, getVisitorCode } = this.props;
        const visitorCode = getVisitorCode('example.com');
    
        addData(visitorCode, addPageView('example.com', 'title', 3));
      }
    
      ...
    }
    
    export default compose(
      withAddData,
      withPageView,
      withVisitorCode,
    )(MyComponent);
    

    An alternative for usePageView hook.

    Arguments
    Returns

    useConversion

    import { useEffect } from 'react';
    import { useAddData, useConversion, useVisitorCode } from '@kameleoon/react-sdk';
    
    function MyComponent(): JSX.Element {
      const { addData } = useAddData();
      const { getVisitorCode } = useVisitorCode();
      const { addConversion } = useConversion();
    
      useEffect(() => {
        const visitorCode = getVisitorCode('example.com');
    
        addData(visitorCode, addConversion(32, 10, false));
      }, []);
    
      ...
    }
    

    useConversion hook returns a callback function addConversion() used to structure conversion data.

    Callback Arguments
    Callback Returns
    Types

    withConversion

    import {
      withVisitorCode,
      withAddData,
      withConversion,
      compose,
    } from '@kameleoon/react-sdk';
    
    class MyComponent extends React.Component {
      componentDidMount() {
        const { addData, addConversion, getVisitorCode } = this.props;
        const visitorCode = getVisitorCode('example.com');
    
        addData(visitorCode, addConversion(32, 10, false));
      }
    
      ...
    }
    
    export default compose(
      withAddData,
      withConversion,
      withVisitorCode,
    )(MyComponent);
    

    An alternative for useConversion hook.

    Arguments
    Returns

    useDevice

    import { useEffect } from 'react';
    import { useAddData, useDevice, useVisitorCode, DeviceType } from '@kameleoon/react-sdk';
    
    function MyComponent(): JSX.Element {
      const { addData } = useAddData();
      const { getVisitorCode } = useVisitorCode();
      const { addDevice } = useDevice();
    
      useEffect(() => {
        const visitorCode = getVisitorCode('example.com');
    
        addData(visitorCode, addDevice(DeviceType.Desktop));
      }, []);
    
      ...
    }
    

    useDevice hook returns a callback function addDevice() used to structure device data.

    Callback Arguments
    Callback Returns
    Types

    withDevice

    import {
      withVisitorCode,
      withAddData,
      withDevice,
      DeviceType,
      compose,
    } from '@kameleoon/react-sdk';
    
    class MyComponent extends React.Component {
      componentDidMount() {
        const { addData, addDevice, getVisitorCode } = this.props;
        const visitorCode = getVisitorCode('example.com');
    
        addData(visitorCode, addDevice(DeviceType.Desktop));
      }
    
      ...
    }
    
    export default compose(
      withAddData,
      withDevice,
      withVisitorCode,
    )(MyComponent);
    

    An alternative for useDevice hook.

    Arguments
    Returns

    useCustomData

    import { useEffect } from 'react';
    import { useAddData, useCustomData, useVisitorCode } from '@kameleoon/react-sdk';
    
    function MyComponent(): JSX.Element {
      const { addData } = useAddData();
      const { getVisitorCode } = useVisitorCode();
      const { addCustomData } = useCustomData();
    
      useEffect(() => {
        const visitorCode = getVisitorCode('example.com');
    
        addData(visitorCode, addCustomData(1, 'some custom value'));
      }, []);
    
      ...
    }
    

    useCustomData hook returns a callback function addCustomData used to structure custom data.

    Arguments
    Returns
    Types

    withCustomData

    import {
      withVisitorCode,
      withAddData,
      withCustomData,
      compose,
    } from '@kameleoon/react-sdk';
    
    class MyComponent extends React.Component {
      componentDidMount() {
        const { addData, addCustomData, getVisitorCode } = this.props;
        const visitorCode = getVisitorCode('example.com');
    
        addData(visitorCode, addCustomData(1, 'some custom value'));
      }
    
      ...
    }
    
    export default compose(
      withAddData,
      withCustomData,
      withVisitorCode,
    )(MyComponent);
    

    An alternative for useAddData hook.

    Arguments
    Returns

    Compose

    import { withVisitorCode, withTriggerExperiment, compose } from '@kameleoon/react-sdk';
    
    class MyComponent extends React.Component {
      componentDidMount() {
        const { getVisitorCode, getVariationId } = this.props;
        const visitorCode = getVisitorCode('example.com');
        const experimentId = 230243;
    
        const variationId = getVariationId(visitorCode, experimentId);
      }
    
      ...
    }
    
    export default compose(withVisitorCode, withTriggerExperiment)(MyComponent);
    

    compose() is a helper higher-order component used for easier composition.

    Arguments
    Returns

    Technical Considerations

    Kameleoon made an important architectural design decision with its SDK technology, namely that every SDK must comply with a zero latency policy. In practice, this means that any blocking remote server call should be banned, as even the fastest remote call would add a 20ms latency to your application. And if for any reason our servers are slower to reply than usual (unfortunately, this can happen), this delay can quickly increase to hundreds of milliseconds, seconds... or even completely block the run of your web-application for the end user. We believe that web performance is of paramount importance in today's world and we don't think adding A/B testing or feature flagging capabilities should come at the cost of a slow down in your web-application.

    However, in the case of the React SDK, since it runs on users' devices, the configuration of all active experiments / feature flags has to be fetched at the start of the session. It cannot be fetched once for every , similarly to the server-side case. This introduces a mandatory distant server call with an associated callback, which means code using the React SDK (implementing experiments or feature flags) cannot be ran prior to the execution of this callback. For maximal performance, this can be avoided by using our Activation API in tandem with a standard Kameleoon install, where the configuration data is directly embedded in the Kameleoon application file (i.e. the JavaScript file contains both the engine code and the configuration of active experiments). But this is a completely different process (advantages of each method are highlighted here), with a different API and no npm package (installation is done by integrating a standard JS tag on the HTML source page). It is described on this article.

    Apart from this initial remote call, we guarantee that the use of our SDKs has absolutely no performance impact. Variation allocation for experiments takes place directly on the SDK code (without any distant API call), and tracking calls are made asynchronously in a non-blocking way (in the background).

    Having a zero latency policy also imposes some other constraints. The main one is that user data about your visitor should be kept locally, and not fetched from a remote server. For instance, if you set a custom data for a given visitor, we must store this somehow in the user's device. In the case of the React SDK, this is implemented via a map of visitor data directly on the browser LocalStorage. So if you use addCustomData and then addData methods, the information will be stored in the browser's LocalStorage. It should usually not grow too big in size, unless you use an extremely large amount of custom data.

    Overview

    When you update your experiment or feature flag configuration, the SDK can get it in two different ways. The first - polling - consists in retrieving the configuration at regular intervals. The second - streaming - is to retrieve the new configuration immediately. The way it's done can be changed in the Administration section on Kameleoon Back-Office.

    Polling

    This mode of obtaining the configuration is used by default. The SDK will send a request to Cloudflare CDN at regular intervals to retrieve the configuration. If you have not set an interval in the SDK configuration, the configuration in SDK will be refreshed every 60 minutes. It can be configured with actions_configuration_refresh_interval parameter.

    Streaming

    This mode allows the SDK to automatically take into account the new configuration without delay. When turned on, Kameleoon SDK is notified of any changes to the configuration in real-time, thanks to server-sent events (SSE).

    If you rely on instant data updates (real-time), then streaming is for you. If you wish to activate this mode, please contact us.

    React Native Considerations

    Visitor Code Generation

    React Native uses AsyncStorage to store feature flag data so getVisitorCode callback retrieved from useVisitorCode hook won't be able to obtain visitor code safely, instead there are dedicated getAsyncVisitorCode callback that will retrieve visitor code asynchronously. The following hook and HOC are meant for retrieving the concerning callback.

    useAsyncVisitorCode

    import { useState } from 'react';
    import { uuidv4 } from 'uuid';
    import { useAsyncVisitorCode } from '@kameleoon/react-sdk';
    
    function MyComponent(): JSX.Element {
      const { getAsyncVisitorCode } = useAsyncVisitorCode();
      const [visitorCode, setVisitorCode] = useState<string>('');
    
      async function retrieveVisitorCode(): Promise<void> {
        // Without defaultVisitorCode argument
        const result = await getAsyncVisitorCode('example.com');
    
        // With defaultVisitorCode argument
        const result = await getAsyncVisitorCode('example.com', uuidv4());
    
        setVisitorCode(result);
      }
      ...
    }
    

    useAsyncVisitorCode returns a callback function getAsyncVisitorCode() used to obtain visitor code asynchronously.

    Callback Arguments
    Callback Returns

    withAsyncVisitorCode

    import { uuidv4 } from 'uuid';
    import { withAsyncVisitorCode } from '@kameleoon/react-sdk';
    
    class MyComponent extends React.Component {
      async componentDidMount(): Promise<void> {
        const { getAsyncVisitorCode } = this.props;
    
        // Without defaultVisitorCode argument
        const visitorCode = await getAsyncVisitorCode('example.com');
    
        // With defaultVisitorCode argument
        const visitorCode = await getAsyncVisitorCode('example.com', uuidv4());
      }
    
      ...
    }
    
    export default withVisitorCode(MyComponent);
    

    An alternative for useAsyncVisitorCode hook.

    Arguments
    Returns

    Common Methods Usage

    import { useState, useEffect } from 'react';
    import {
      useActivateFeature,
      useRunWhenReady,
      useAsyncVisitorCode,
    } from '@kameleoon/react-sdk';
    
    function MyComponent(): JSX.Element {
      const featureKey = 'example-feature-key';
    
      const [visitorCode, setVisitorCode] = useState<string>('');
      const { getAsyncVisitorCode } = useAsyncVisitorCode();
    
      async function updateVisitorCode(): Promise<void> {
        const result = await getAsyncVisitorCode('example.com');
        setVisitorCode(result);
      }
    
      useEffect(() => {
        updateVisitorCode();
      }, [updateVisitorCode]);
    
      useEffect(() => {
        runWhenReady(
          () => {
            setIsFeature(hasFeature(featureKey, visitorCode));
          },
          () => {},
        );
      }, [hasFeature, visitorCode, setIsFeature]);
    }
    

    For the same reason of using AsyncStorage it is preferable to use all the other sdk methods by wrapping their callback calls in runWhenReady function, which will set a timeout making sure that all the asynchronous data flow will be processed correctly. The code example demonstrate the best way to utilize useActivateFeature callback for React Native application. All the other callbacks can be handled in the similar fashion.