React Interface
Installation
Install DriftDB-React
using npm
:
npm i driftdb-react
To get started with DriftDB-React, you will need the URL of a running DriftDB server. Either follow the instructions to run a server of your own, or sign up at Jamsocket.live for a free hosted instance.
Importing
Import the hooks and provider as necessary, like this:
import { useSharedReducer, StatusIndicator, useUniqueClientId, usePresence, DriftDBProvider } from 'driftdb-react'
Connecting
DriftDB-React makes use of two react patterns: a provider to connect to the database, and hooks to access it. DriftDB-React exports the DriftDBProvider
component, which instantiates a database connection.
Components must be enclosed in a DriftDBProvider
component to use the hooks that DriftDB-React provides (this approach ensures that all hooks share the underlying connection). You should not use multiple DriftDBProvider
instances on the same page; instead, put a single DriftDBProvider
component high enough up in the document tree that all components which need to use the database are children of it.
The provider takes a required parameter api
, which must be the URL of a running DriftDB server. This can either be one that you run on your own, or one provided by Jamsocket Live.
<DriftDBProvider api="https://jamsocket.live/db/[YOUR_KEY]">
{
// Components in here (and their descendants)
// can use DriftDB-React hooks.
}
</DriftDBProvider>
If you don’t explicitly pass a room
parameter and there is not one already in the URL, DriftDBProvider
will talk to the API to create a room, and add it to the current location URL. Alternatively, you can talk to the API on your own (possibly in the backend) to create a room, and pass it as the room
parameter. If you explicitly pass in a room
, DriftDBProvider
will not modify the current URL.
useConnectionStatus
hook
The useConnectionStatus
hook returns an object with the current connection status of the database. If the status is connected, it also returns a link to a debug UI for the room.
The StatusIndicator
component uses this hook to display the current connection status, including a link to inspect the database in the DriftDB debug UI. This is meant for development; in a real application, you could replace this with a custom component that suits your UI using the output of useConnectionStatus
.
useSharedState
hook
DriftDB-React provides a hook called useSharedState
, which is similar to the useState
hook provided by React, except that it synchronizes state across all clients connected to the same room.
In order to do this, useSharedState
adds an additional parameter, which is a key in the associated DriftDB room.
Here’s an example of using useSharedState
to create a checkbox that is synchronized across all clients in the same room:
function MyComponent() {
const [value, setValue] = useSharedState("my-checkbox", false);
return <input
type="checkbox"
checked={value}
onChange={e => setValue(e.target.checked)}
/>;
}
useSharedState
is debounced on the client side, meaning that if you make calls to it in rapid succession, it will wait for a short period and send only the last value to the server. useSharedState
is optimistic, so calls to setValue
will be reflected locally before they are confirmed by the server.
Like React's useState
, the setter function returned by useSharedState
can itself take a function as an argument. This function will be called with the current value of the state, and the value it returns will become the new state. Note that this uses the latest state on the client, so it’s possible for it to clobber an update from another client that hasn’t reached the current client yet. To avoid this, prefer useSharedReducer
, which guarantees that all updates are applied.
useSharedReducer
hook
There are two major limitations to useSharedState
:
- The entire state is sent on every update. This is fine for small values, but does not scale well to more complex state.
- It overwrites the entire state on every update. If two clients try to update the state at the same time, one of them will be overwritten.
To address these limitations, DriftDB-React provides a hook called useSharedReducer
, which is a shared version of React’s useReducer
hook. Like useSharedState
, useSharedReducer
adds an additional key
parameter as the first argument.
Here’s an example of using useSharedReducer
to implement a counter that is synchronized across all clients in the same room:
function CounterComponent() {
const [count, dispatch] = useSharedReducer("my-counter", (state, action) => {
switch (action.type) {
case "increment":
return state + 1;
case "decrement":
return state - 1;
default:
return state;
}
}, 0);
return (
<div>
<button onClick={() => dispatch({ type: "decrement" })}>-</button>
<span>{count}</span>
<button onClick={() => dispatch({ type: "increment" })}>+</button>
</div>
);
}
Like useSharedState
, useSharedReducer
is optimistic, so calls to dispatch
will be reflected locally before they are confirmed by the server. useSharedReducer
guarantees that the same change will be applied in the same order across all clients. To make this work, it may need to rewind a local optimistic change to reapply it in the correct order.
useSharedReducer
is possible because DriftDB allows keys to have a sequence of values, instead of just one. Changes are appended to the end of the sequence, instead of replacing the previous value as useSharedState
does. When a new client joins, it plays back the entire sequence of changes to bring it up to date.
To avoid the sequence from growing indefinitely, useSharedReducer
uses cooperative compaction. Clients coordinate with the database so that when the sequence for a given key grows too long, the client will replace it with a snapshot of the state at a more recent version, and instructs the server to discard all changes prior to that version.
usePresence
hook
The usePresence
hook takes a value corresponding to the active client (e.g. current cursor position) and returns an object containing the values that other clients sent. The keys of the object are unique client IDs, the same as returned by useUniqueClientId
.
useDatabase
hook
The useDatabase
hook returns an instance of the DriftDB database itself. This is useful if you want to access the database on a lower level than is exposed through the other hooks.
useUniqueClientId
hook
The useUniqueClientId
hook returns a short string which can be used to uniquely identify the current client. This string is stored in session storage, so that it will persist across page reloads.
useUniqueClientId
is provided as a helper, but does not interact with the DriftDB database.
<RoomQRCode />
component
The RoomQRCode
component displays a QR code encoding the URL of the current page, if it contains the ID of a DriftDB room. This provides an easy way to connect to rooms from a phone.