React
Bunshi ships with support for React out of the box.
import { useMolecule, ScopeProvider, MoleculeProvider } from "bunshi/react";
Basic API
useMolecule
Use a molecule for the current scope. Will produce a different value depending on the React context it is run in.
import { useMolecule } from "bunshi/react";
import { useSetAtom, useAtomValue } from "jotai";
export const PageComponent = () => {
const pageAtoms = useMolecule(PageMolecule);
const setParams = useSetAtom(pageAtoms.setParams);
const page = useAtomValue(pageAtoms.currentPage);
return (
<div>
Page: {page}
<br />
<button onClick={() => setParams({ date: Date.now() })}>
Set current time
</button>
</div>
);
};
By default useMolecule will provide a molecule based off the implicit scope from context. You can override this behaviour by passing options to useMolecule.
withScope- will overide a scope valuewithUniqueScope- will override a scope value with a new unique valueexclusiveScope- will override ALL scopes
Instead of a scope provider, you can use an explicit scope when using a molecule. This can simplify integrating with other libraries.
Implicit scope:
const App = () => (
<ScopeProvider scope={UserScope} value={"sam@example.com"}>
<UserComponent />
</ScopeProvider>
);
Explicit scope:
useMolecule(UserMolecule, { withScope: [UserScope, "sam@example.com"] });
ScopeProvider
Provides a new value for Scope, similar to React Context. This will create new molecules in the react tree that depend on it.
import { ScopeProvider } from "bunshi/react";
const App = () => (
<ScopeProvider scope={UserScope} value={"sam@example.com"}>
<UserComponent />
</ScopeProvider>
);
scopetheMoleculeScopereference to providevaluea new value for that scope
Advanced API
MoleculeProvider
Provides implementations for molecule interfaces throughout the React component tree, allowing you to supply concrete molecules for abstract interfaces.
// Define interface and implementation
const SendsEmailMolecule = moleculeInterface<{
sendEmail(recipient: string);
}>();
const SendGmailMolecule = molecule(() => ({
sendEmail: (recipient: string) => { /* ... */ },
}));
// Provide implementation
const App = () => (
<MoleculeProvider interface={SendsEmailMolecule} value={SendGmailMolecule}>
<EmailForm />
</MoleculeProvider>
);
Interface Resolution in Child Molecules
Child molecules can resolve interfaces while preserving existing molecule instances:
// Parent molecule (created once, shared everywhere)
const CacheMolecule = molecule(() => new Map());
// Molecule using interfaces
const UserServiceMolecule = molecule((get) => {
const cache = get(CacheMolecule); // Existing molecule (not re-created)
const db = get(DatabaseInterface); // Resolved via provider
const logger = get(LoggerInterface); // Resolved via provider
return {
async getUser(id: string) {
const cached = cache.get(id);
if (cached) return cached;
// ... fetch and cache
}
};
});
const App = () => (
<MoleculeProvider interface={DatabaseInterface} value={SqliteDatabaseMolecule}>
<MoleculeProvider interface={LoggerInterface} value={ConsoleLevelMolecule}>
<UserProfile userId="123" />
</MoleculeProvider>
</MoleculeProvider>
);
Providers create child injectors that inherit the parent cache, preserving existing molecules while enabling interface resolution.