Skip to main content
For AI agents: a documentation index is available at /llms.txt

Component State

Use useModel(model, selector?, depends?) inside a DouraRoot tree when the component should read or update the shared store.

import { defineModel } from 'doura'
import { useModel } from 'react-doura'

const countModel = defineModel({
name: 'count',
state: {
count: 0,
},
actions: {
inc() {
this.count += 1
},
},
})

function Counter() {
const counter = useModel(countModel)

return <button onClick={() => counter.inc()}>Count: {counter.count}</button>
}

useModel returns the model API directly. It does not take a separate name and does not return a tuple.

Selectors

Use a selector when a component only needs part of the model API:

const userModel = defineModel({
name: 'user',
state: {
name: 'alice',
isLogin: false,
},
actions: {
login() {
this.isLogin = true
},
},
})

function Login() {
const { isLogin, login } = useModel(
userModel,
(api, actions) => ({
isLogin: api.isLogin,
login: actions.login,
}),
[]
)

return isLogin ? <div>Welcome</div> : <button onClick={login}>Login</button>
}

The optional depends array controls when an inline selector is recreated. For a module-level selector, no dependency array is needed:

import type { Selector } from 'react-doura'

const selector: Selector<typeof userModel> = (api, actions) => ({
isLogin: api.isLogin,
login: actions.login,
})

function Login() {
const { isLogin, login } = useModel(userModel, selector)
return isLogin ? <div>Welcome</div> : <button onClick={login}>Login</button>
}

Isolated Component State

Use useDetachedModel when each component instance should own an independent model store:

import { useDetachedModel } from 'react-doura'

function LocalCounter() {
const counter = useDetachedModel(countModel)

return (
<button onClick={() => counter.inc()}>Local count: {counter.count}</button>
)
}

Detached models are not included in a parent store's getState().