⚙️ Middleware Pipline ( redux-devtools support … )
☂️ 100% test coverage, safe on production
🐛 Debug easily on test environment
import { Model } from 'react-model'
// define model
const Todo = {
state: {
items: ['Install react-model', 'Read github docs', 'Build App']
},
actions: {
add: todo => {
// s is the readonly version of state
// you can also return partial state here but don't need to keep immutable manually
// state is the mutable state
return state => {
state.items.push(todo)
}
}
}
}
// Model Register
const { useStore } = Model(Todo)
const App = () => {
return <TodoList />
}
const TodoList = () => {
const [state, actions] = useStore()
return <div>
<Addon handler={actions.add} />
{state.items.map((item, index) => (<Todo key={index} item={item} />))}
</div>
}
react-model keeps the application state and actions in separate private stores. So you need to register them if you want to use them as the public models.
model/index.ts
import { Model } from 'react-model'
import Home from '../model/home'
import Shared from '../model/shared'
const models = { Home, Shared }
export const { getInitialState, useStore, getState, actions, subscribe, unsubscribe } = Model(models)
We always want to try catch all the actions, add common request params, connect Redux devtools and so on. We Provide the middleware pattern for developer to register their own Middleware to satisfy the specific requirement.
const Component = () => {
// 3.1.x, Component rerender when add action is invoked
const [counter] = useStore('Shared', ['add'])
// 4.x.x, Component rerender when counter value diff
const [counter] = useStore('Shared', state => state.counter)
}
How can I disable the console debugger
import { middlewares } from 'react-model'
// Find the index of middleware
// Disable all actions' log
middlewares.config.logger.enable = false
// Disable logs from specific type of actions
middlewares.config.logger.enable = ({ actionName }) => ['increment'].indexOf(actionName) !== -1
import { actionMiddlewares, Model } from 'react-model'
import Example from 'models/example'
// Example, not recommend to use on production directly without consideration
// Write current State to localStorage after action finish
const persistMiddleware: Middleware = async (context, restMiddlewares) => {
localStorage.setItem('__REACT_MODEL__', JSON.stringify(context.Global.State))
await context.next(restMiddlewares)
}
// Use on all models
actionMiddlewares.push(persistMiddleware)
Model({ Example }, JSON.parse(localStorage.getItem('__REACT_MODEL__')))
// Use on single model
const model = {
state: JSON.parse(localStorage.getItem('__REACT_MODEL__'))['you model name']
actions: { ... },
middlewares: [...actionMiddlewares, persistMiddleware]
}
interface State {
count: number
}
interface ActionParams {
increment: number
}
const model: ModelType<State, ActionParams> = {
state: {
count: 0
},
actions: {
increment: payload => {
// immer.module.js:972 Uncaught (in promise) Error: An immer producer returned a new value *and* modified its draft. Either return a new value *or* modify the draft
// Not allowed
// return state => (state.count += payload)
return state => {
state.count += payload
}
}
}
}
immer.module.js:972 Uncaught (in promise) Error: An immer producer returned a new value *and* modified its draft. Either return a new value *or* modify the draft
How to fix:
actions: {
increment: payload => {
// Not allowed
// return state => (state.count += payload)
return state => {
state.count += payload
}
}
}
react-model ·

The State management library for React
🎉 Support Both Class and Hooks Api
⚛️ Support preact, react-native and Next.js
⚔ Full TypeScript Support
📦 Built with microbundle
⚙️ Middleware Pipline ( redux-devtools support … )
☂️ 100% test coverage, safe on production
🐛 Debug easily on test environment
Recently Updated
Quick Start
CodeSandbox: TodoMVC
Next.js + react-model work around
v2 docs
install package
Table of Contents
Core Concept
Model
Every model has its own state and actions.
⇧ back to top
Model Register
react-model keeps the application state and actions in separate private stores. So you need to register them if you want to use them as the public models.
model/index.ts⇧ back to top
useStore
The functional component in React ^16.8.0 can use Hooks to connect the global store. The actions returned from useStore can invoke dom changes.
The execution of actions returned by useStore will invoke the rerender of current component first.
It’s the only difference between the actions returned by useStore and actions now.
optional solution on huge dataset (example: TodoList(10000+ Todos)):
advance example with 1000 todo items
⇧ back to top
getState
Key Point: State variable not updating in useEffect callback
To solve it, we provide a way to get the current state of model: getState
Note: the getState method cannot invoke the dom changes automatically by itself.
⇧ back to top
actions
You can call other models’ actions with actions api
actions can be used in both class components and functional components.
⇧ back to top
subscribe
subscribe(storeName, actions, callback) run the callback when the specific actions executed.
⇧ back to top
Advance Concept
immutable Actions
The actions use immer produce API to modify the Store. You can return a producer in action.
Using function as return value can make your code cleaner when you modify the deep nested value.
TypeScript Example
JavaScript Example
⇧ back to top
SSR with Next.js
Store: shared.ts
Global Config: _app.tsx
Page: hooks/index.tsx
Single Page Config: benchmark.tsx
⇧ back to top
Middleware
We always want to try catch all the actions, add common request params, connect Redux devtools and so on. We Provide the middleware pattern for developer to register their own Middleware to satisfy the specific requirement.
⚙️ You can override the actionMiddlewares and insert your middleware to specific position
⇧ back to top
Expand Context
⇧ back to top
Other Concept required by Class Component
Provider
The global state standalone can not effect the react class components, we need to provide the state to react root component.
⇧ back to top
connect
We can use the Provider state with connect.
Javascript decorator version
TypeScript Version
⇧ back to top
FAQ
Migrate from 3.1.x to 4.x.x
sub-model.tsmodels.tsShared.tsHow can I disable the console debugger
⇧ back to top
How can I add custom middleware
⇧ back to top
How can I make persist models
⇧ back to top
How can I deal with local state
What should I do to make every Counter hold there own model? 🤔
Counter model
Counter.tsx
⇧ back to top
actions throw error from immer.module.js
How to fix:
⇧ back to top
How can I customize each model’s middlewares?
You can customize each model’s middlewares.
⇧ back to top