Skip to main content

React Doura

useModel

Types

declare interface UseModel extends UseAnonymousModel, UseNamedModel {}
caution

With the param name or not, useModel has very different behavior.

useModel Without Name

useModel can replace useState, and enjoy doura features.

Types

declare interface UseAnonymousModel {
<IModel extends AnyModel>(model: IModel): ModelAPI<IModel>
<IModel extends AnyModel, S extends Selector<IModel>>(
model: IModel,
selectors: S,
depends?: any[]
): ReturnType<S>
}

Example

Easy to replace useState, no need care about warp add with useCallback.

const count = defineModel({
state: {
value: 1,
},
actions: {
add(payload: number = 1) {
this.value += payload
},
},
})
const App = () => {
const counter = useModel(count)

return (
<>
<div id="value">{counter.value}</div>
<button id="button" type="button" onClick={() => counter.add()}>
add
</button>
</>
)
}

Use selector to pick exact what we want

Selector Types

type Selector<Model extends AnyModel, TReturn = any> = (
api: ModelAPI<Model>,
actions: ModelActions<Model>
) => TReturn

Example

import { Selector } from 'react-doura'
const countSelector: Selector<
typeof countModel,
{ count: number; add: () => void }
> = (s, actions) => {
return { count: s.count, add: actions.add }
}
import { ModelAPI, ModelActions } from 'doura'
const countSelector = (
s: ModelAPI<typeof countModel>,
actions: ModelActions<typeof countModel>
) => {
return { count: s.count, add: actions.add }
}

when depends not changed, the selectors function will be old one, it works like useCallback.

const countModel = defineModel({
state: {
value: 1,
},
actions: {
add(payload: number = 1) {
this.value += payload
},
async asyncAdd(n: number) {
await sleep(200)
this.add(n)
},
},
views: {
test() {
return this.value + 1
},
},
})

const App = () => {
const counter = useModel(
countModel,
(state, actions) => {
return {
value: state.value,
test: state.test,
...actions,
}
},
[]
)

return (
<>
<div id="v">{counter.value}</div>
<div id="t">{counter.test}</div>
<button id="button" type="button" onClick={() => counter.add(2)}>
add
</button>
</>
)
}

DouraRoot

Provider context for useModel and useStaticModel.

Types

declare const DouraRoot: (
props: PropsWithChildren<{
store?: Doura
}>
) => JSX.Element

Example

<DouraRoot store={doura()}>
<App />
</DouraRoot>

useModel With Name

Get global state by anywhere.

Types

declare interface UseNamedModel {
<IModel extends AnyModel>(name: string, model: IModel): ModelAPI<IModel>
<IModel extends AnyModel, S extends Selector<IModel>>(
name: string,
model: IModel,
selectors: S,
depends?: any[]
): ReturnType<S>
}

Example

const App = () => {
const counter = useModel(
'count',
countModel,
(s, actions) => {
fn()
return { count: s.count, add: actions.add }
},
[]
)

return (
<button id="count" onClick={() => counter.add()}>
{counter.count}
</button>
)
}

useStaticModel

caution

State change will not trigger render.

Types

declare interface UseStaticModel {
<IModel extends AnyModel>(name: string, model: IModel): ModelAPI<IModel>
}

Example

const model = defineModel({
state: { value: 1 },
views: {
test() {
return this.value * 2
},
},
})

const App = () => {
const state = useStaticModel('test', model)

return (
<>
<div id="v">{state.value}</div>
<div id="t">{state.test}</div>
</>
)
}

createContainer

Create a group api for share states.

Types

declare const createContainer: (options?: DouraOptions) => {
Provider: (
props: PropsWithChildren<{
store?: Doura
}>
) => JSX.Element
useSharedModel: UseNamedModel
useStaticModel: UseStaticModel
}

Example

const {
Provider, // context as same as DouraRoot
useSharedModel, // as same as useModel and it first param must be `name`
useStaticModel, // as same as useStaticModel
} = createContainer()