Using the React SDK
Overview
This guide will walk you through the tools that the React SDK provides and examples on how to use them to build an onboarding experience.
Before we get started, you'll need:
- A blocks API key
- A flow identifier and version number
- A block identifier
Installation
You can install the React SDK into your React project with a JavaScript package manager:
- npm
- Yarn
- pnpm
npm install @dopt/react
yarn add @dopt/react
pnpm add @dopt/react
Usage
The React SDK consists of three parts: a provider, hooks, and higher-order components (HOCs).
DoptProvider
The DoptProvider
allows you to leverage Dopt hooks and HOCs by providing access to the state of step blocks for a given flow version in the context of an identified user.
Here’s what the provider might look like in your app:
<DoptProvider
flowVersions={{
'user-onboarding': 2,
'upsell': 4,
'feature-callout': 0,
}}
userId={userId}
apiKey={blocksAPIKey}
>
<Application />
</DoptProvider>
While it’s common to place the provider at the root of your app, you can place it anywhere you’d like as long as it comes before where you would use useBlock
or withBlock
. You can even have multiple instances of the provider in various places in your app.
useBlock
hook
The useBlock
hook helps you bind block state and content to your UI and also gives you access to methods for mutating block state.
Here’s how using the hook might look like in your app for the twenty-llamas-attack
block in the new-user-onboarding
flow:
const [block, transition] = useBlock<['complete']>(
'new-user-onboarding.twenty-llamas-attack'
);
The <['complete']>
portion in the useBlock
instantiation is a TypeScript specific concept which allows you to enumerate the transitions associated with the block. This provides a bunch of utility in terms of error detection and code completion while developing with the SDK.
The example above uses the block identifier (twenty-llamas-attack
) prefixed with the flow identifier (new-user-onboarding
). However, you can also use the uid
for the block without the flow identifier prefix instead.
You can further destructure the block
variables to expose a subset of block data:
const [{ state, field }, transition] = useBlock<['complete']>(
'new-user-onboarding.twenty-llamas-attack'
);
useFlow
hook
The useFlow
hook helps you bind flow state to your UI and also gives you access to methods for mutating flow state.
Here’s how using the hook might look like in your app for the new-user-onboarding
flow:
const [flow, intent] = useFlow('new-user-onboarding');
You can further destructure the flow
and intent
variables to expose a subset of flow data and intent methods:
const [{ blocks, version, state }, { finish, reset }] = useFlow(
'new-user-onboarding'
);
Example
Let’s walk through an example of how you might use the React SDK to build an onboarding experience.
Initializing DoptProvider
First, we’ll need to initialize the DoptProvider
in our app.
import { DoptProvider } from '@dopt/react';
import Application from './application';
const rootElement = document.getElementById('root');
ReactDOM.render(
<DoptProvider
userId="0001"
apiKey="YOUR_BLOCKS_API_KEY"
flowVersions={{ 'new-user-onboarding': 2 }}
>
<Application />
</DoptProvider>,
rootElement
);
We’ve hardcoded the user identifier (0001
) for demonstration purposes in the userId
prop. Realistically, you should dynamically pass in the user identifier that you used to identify the user who will be seeing this onboarding experience.
We’ve added our blocks API key to the apiKey
prop.
And finally, we’ve defined that we want to pull in version 2
of the new-user-onboarding
flow in the flowVersions
prop. Learn more about flow versioning →
If you’d like to pull in the uncommitted version of a flow, you can set the version tag to 0
. This can help while you’re actively developing.
Reacting to block state
Now that we’ve added DoptProvider
to our app, we can use the useBlock
hook to bind our UI to block state.
Let’s walk through an example of conditionally rendering a modal based off of the active
state of a step block. Basically, this boils down to showing the modal when the step block active
state is true
and hiding it when the active
state is false
.
This example showcases reacting to the active
block state, but you can hook up your UI to the entered
and exited
states as well.
The useBlock
hook helps us access the active
state:
import { useBlock } from '@dopt/react';
import { Modal } from './modal';
export function Application() {
const [{ state }] = useBlock<['complete']>(
'new-user-onboarding.twenty-llamas-attack'
);
return (
<main>
{state.active && (
<Modal>
<h1>👏 Welcome to our app!</h1>
<p>This is your onboarding experience!</p>
</Modal>
)}
</main>
);
}
We’ve pulled in the state
of the twenty-llamas-attack
step block. The <Modal />
is then set to conditionally render if state.active
evaluates to true
.
Mutating block state
Now that we’ve hooked up our modal to react to block state, let’s also set it up so that clicking a button hides the modal. Imagine that we’ve created a path named complete
from the twenty-llamas-attack
step block to the next block in our flow.
Let’s build off of our previous code using the useBlock
hook:
import { useBlock } from '@dopt/react';
import { Modal } from './modal';
export function Application() {
const [{ state }, transition] = useBlock<['complete']>(
'new-user-onboarding.twenty-llamas-attack'
);
return (
<main>
{state.active && (
<Modal>
<h1>👏 Welcome to our app!</h1>
<p>This is your onboarding experience!</p>
<button onClick={() => transition('complete')}>
Close me
</button>
</Modal>
)}
</main>
);
}
We’ve made two changes to the original example. First, we're pulling in the transition()
function from the useBlock
hook. This allows us to transition the twenty-llamas-attack
step block along it’s complete
path which in turn will also update the block’s state to active: false
and exited: true
.
Second, we’ve added a <button />
with an onClick
handler that is hooked up to the transition()
function. Within the transition()
function, we are passing in our complete
path name to indicate that the user should transition along the complete
path. This translates to a click on the button setting the active
state to false
which will hide our modal.
Calling transition('complete')
will also update the block’s transitioned
data to { complete: true }
to reflect that the block was transitioned along its complete
path.
Using fields
Pulling down field values is just as easy as using state. Let's imagine that we have configured text fields for a title
, body
, and button
for our modal. We can access these fields using the field()
accessor from the useBlock
hook.
import { useBlock } from '@dopt/react';
import { Modal } from './modal';
export function Application() {
const [{ state, field }, transition] = useBlock<['complete']>(
'new-user-onboarding.twenty-llamas-attack'
);
return (
<main>
{state.active && (
<Modal>
<h1>{field('title')}</h1>
<p>{field('body')}</p>
<button onClick={() => transition('complete')}>
{field('button')}
</button>
</Modal>
)}
</main>
);
}
Note how we've replaced all of the static content with field()
that references the field's identifier. When this modal renders, the field values that we define in Dopt will show up for in the <h1>
, <p>
, and <button>
.