Orchestration SDKs

Starting a DaVinci flow in React Native

PingOne React Native

The recommended way to start and navigate a DaVinci flow in a React Native component is with the useDaVinci hook. It returns an object containing the current node and a set of actions.

Use the start() action and pass a configured client to start a DaVinci flow:

Starting a flow with useDaVinci
const { node, start, loading, error } = useDaVinci(davinciClient);

useEffect(() => {
  start().catch(() => {}); // error surfaced via hook state
}, [start]);

If you call useDaVinci() without an argument, it uses the shared client from a surrounding DaVinciProvider.

The hook stores the returned node and sets loading while start() or next() is running.

The error property contains the most recent hook-level error, or null when no hook error has occurred.

The auto-start effect catches the promise rejection because start() rejects when the flow cannot start. The hook records the failure in its error state, so the catch block can remain empty.

Error handling

The start() action can reject when the module cannot configure or start the DaVinci flow.

The React Native module rejects failed operations with a DaVinciError, which extends PingError and ultimately extends the standard JavaScript Error class.

Use instanceof DaVinciError to identify the error and inspect its code, type, and message properties:

Handling an error when starting a DaVinci flow
import { DaVinciError, useDaVinci } from '@ping-identity/rn-davinci';
import { Button} from 'react-native';
function LoginScreen() {
  const { start } = useDaVinci(davinciClient);

  async function beginFlow() {
    try {
      await start();
    } catch (err) {
      if (err instanceof DaVinciError) {
        console.error(err.code, err.type, err.message);
      }
    }
  }

  return <Button title="Sign in" onPress={() => beginFlow()} />;
}

The stable error codes are:

Error code Description

DAVINCI_ARGUMENT_ERROR

The app called an operation with an invalid argument.

DAVINCI_COLLECTOR_APPLY_ERROR

A collector value submitted with next() could not be applied, for example because its key does not match a collector on the current node.

DAVINCI_CONFIG_ERROR

The client configuration is missing or invalid, such as an empty clientId or redirectUri.

DAVINCI_DISPOSE_ERROR

The native flow instance could not be disposed.

DAVINCI_INIT_ERROR

The native DaVinci module could not initialize the flow.

DAVINCI_LOGOUT_ERROR

The user could not be signed out.

DAVINCI_MISSING_INTEGRATION_ERROR

A collector requires an integration, such as social sign-on, that the app has not configured.

DAVINCI_NEXT_ERROR

The flow could not advance to the next node.

DAVINCI_POLL_ERROR

A polling operation could not be started.

DAVINCI_SESSION_ERROR

A session operation, such as refreshing or revoking tokens, failed.

DAVINCI_START_ERROR

The flow could not be started.

DAVINCI_STATE_ERROR

An operation was called in an invalid state, such as calling next() when the flow has no active node.

DAVINCI_UNKNOWN_ERROR

An unexpected error occurred.

DAVINCI_UNSUPPORTED_COLLECTOR_ERROR

The app submitted a value for a collector whose type the bridge does not support, so no value can be applied to it.

Sharing DaVinci state across screens

Each screen that calls useDaVinci(davinciClient) gets its own isolated hook state by default.

For example, a LoginScreen and an MfaScreen would each track their own node, loading, and error values rather than automatically sharing the current flow state.

The DaVinci module provides a DaVinciProvider that holds the configured client and shared hook state for descendant components.

Wrapping your navigator, or your entire app, with DaVinciProvider means that every screen within it can read and update the same flow state when it calls useDaVinci().

This is useful when a flow moves between screens, such as when a login screen collects credentials and a separate screen collects a multi-factor authentication code.

Using DaVinciProvider to share state across screens
import {
  DaVinciProvider,
  useDaVinci,
} from '@ping-identity/rn-davinci';

const davinciClientConfig = {
  modules: {
    oidc: {
      clientId: '6c7eb89a-66e9-ab12-cd34-eeaf795650b2',
      discoveryEndpoint: 'https://auth.pingone.com/3072206d-c6ce-ch15-m0nd-f87e972c7cc3/as/.well-known/openid-configuration',
      redirectUri: 'org.forgerock.demo://oauth2redirect',
    },
  },
};

function App(): React.ReactElement {
  return (
    <DaVinciProvider config={davinciClientConfig}>
      <AuthNavigator />
    </DaVinciProvider>
  );
}

function LoginScreen(): React.ReactElement {
  const { node, start, loading } = useDaVinci();
  return <></>;
}

The provider creates one client from the supplied configuration and shares one hook state instance with its descendant components.

Calling useDaVinci() without either a provider or client raises a DaVinciError.