Setup - React
This page covers the React-specific setup that comes after installing @warp-drive/react and configuring your store.
Provide the Store
Once you have a store class, which Configure The Store creates, React components get it through React context. Wrap the app in <StoreProvider /> and give it that class as its Store prop, and it creates the instance for you. To provide a store that already exists, pass the instance as store instead; see Use a Store You Already Have.
import { StoreProvider } from '@warp-drive/react';
import AppStore from './store';
import { UserList } from './user-list';
export function App() {
return (
<StoreProvider Store={AppStore}>
<UserList />
</StoreProvider>
);
}The provider keeps the instance it created for as long as it receives the same class, so import the class from a module rather than defining it inside a component, where every render would create a new class and a new, empty store. Even with the same class, React may discard the instance and create another: in development builds, Strict Mode creates the store twice and keeps one. If your app must have exactly one store instance, create it yourself and pass it as store.
Any component inside the provider reads the store with useStore. In development builds, calling it outside a <StoreProvider /> throws an error. Production builds skip that check, and it returns null.
import { useStore } from '@warp-drive/react';
import { findRecord } from '@warp-drive/utilities/json-api';
export function ReloadButton({ userId }: { userId: string }) {
const store = useStore();
return (
<button onClick={() => store.request(findRecord('user', userId, { reload: true }))}>
Reload
</button>
);
}The React <Request /> component reads the store the same way when you don't pass it a store prop. Reading the store doesn't re-render a component when its data changes; a <ReactiveContext /> does that, and <Request /> already wraps its content in one. To render a request's loading, error and content states, see Reactive Control Flow.
Use a Store You Already Have
Pass an existing instance as the store prop when something outside React owns the store: a module that creates it once at startup, a test that sets it up, or a host app that mounts React components into part of its page. Every <StoreProvider /> given the same instance provides the same store, so separately mounted React roots share one cache and see the same requests.
import { createRoot } from 'react-dom/client';
import type { Store } from '@warp-drive/core';
import { StoreProvider } from '@warp-drive/react';
import { UserList } from './user-list';
export function mountUserList(element: HTMLElement, store: Store): () => void {
const root = createRoot(element);
root.render(
<StoreProvider store={store}>
<UserList />
</StoreProvider>
);
return () => root.unmount();
}The host app calls mountUserList with the element to render into and its own store, and calls the returned function when it removes that element.
Embedding React in an app built with another framework
One store can drive components from both frameworks, but each framework only re-renders for its own signals. For components in both to re-render when data changes, import @warp-drive/alien-signals/install before either framework's install import, so that both frameworks share its signals graph. Memoized values such as derived fields then only recompute when a signal WarpDrive manages changes, not when state that only a framework tracks, such as an Ember @tracked property, changes. See Can I use multiple frameworks on one page?, and for Ember and React, Using Ember and React on the Same Page. An Ember app doesn't need PolarisMode to share its store with React; see Do I need PolarisMode to share state between Ember and React on the same page?.