@@ -23,7 +23,7 @@ const Todo = {
2323 items: [' Install react-model' , ' Read github docs' , ' Build App' ]
2424 },
2525 actions: {
26- add : ( s , actions , todo ) => {
26+ add : todo => {
2727 // s is the readonly version of state
2828 // you can also return partial state here but don't need to keep immutable manually
2929 // state is the mutable state
@@ -33,14 +33,14 @@ const Todo = {
3333}
3434
3535// Model Register
36- const { useStore } = Model ({ Todo } )
36+ const { useStore } = Model (Todo )
3737
3838const App = () => {
3939 return <TodoList />
4040}
4141
4242const TodoList = () => {
43- const [state, actions] = useStore (' Todo ' )
43+ const [state, actions] = useStore ()
4444 return <div >
4545 <Addon handler = { actions .add } />
4646 { state .items .map ((item , index ) => (<Todo key = { index } item = { item } />))}
@@ -82,13 +82,19 @@ npm install react-model
8282 - [ How can I disable the console debugger?] ( #how-can-i-disable-the-console-debugger )
8383 - [ How can I add custom middleware] ( #how-can-i-add-custom-middleware )
8484 - [ How can I make persist models] ( #how-can-i-make-persist-models )
85+ - [ How can I deal with local state] ( #how-can-i-deal-with-local-state )
86+ - [ actions throw error from immer.module.js] ( #actions-throw-error-from-immer.module.js )
8587
8688## Core Concept
8789
8890### Model
8991
9092Every model have their own state and actions.
9193
94+ <details >
95+ <summary ><del >old model</del > (will be deprecated in v3.0)</summary >
96+ <p >
97+
9298``` typescript
9399const initialState = {
94100 counter: 0 ,
@@ -111,7 +117,7 @@ interface ActionsParamType = {
111117 get: undefined
112118} // You only need to tag the type of params here !
113119
114- const Model : ModelType <StateType , ActionsParamType > = {
120+ const model : ModelType <StateType , ActionsParamType > = {
115121 actions: {
116122 increment : async (state , _ , params ) => {
117123 return {
@@ -144,7 +150,73 @@ const Model: ModelType<StateType, ActionsParamType> = {
144150 state: initialState
145151}
146152
147- export default Model
153+ export default model
154+
155+ // You can use these types when use Class Components.
156+ // type ConsumerActionsType = getConsumerActionsType<typeof Model.actions>
157+ // type ConsumerType = { actions: ConsumerActionsType; state: StateType }
158+ // type ActionType = ConsumerActionsType
159+ // export { ConsumerType, StateType, ActionType }
160+ ```
161+ </p >
162+ </details >
163+
164+ ``` typescript
165+ const initialState = {
166+ counter: 0 ,
167+ light: false ,
168+ response: {}
169+ }
170+
171+ interface StateType = {
172+ counter: number
173+ light: boolean
174+ response: {
175+ code? : number
176+ message? : string
177+ }
178+ }
179+
180+ interface ActionsParamType = {
181+ increment: number
182+ openLight: undefined
183+ get: undefined
184+ } // You only need to tag the type of params here !
185+
186+ const model: NextModelType <StateType , ActionsParamType > = {
187+ actions: {
188+ increment : async (payload , { state }) => {
189+ return {
190+ counter: state .counter + (params || 1 )
191+ }
192+ },
193+ openLight : async (_ , { state , actions }) => {
194+ await actions .increment (1 ) // You can use other actions within the model
195+ await actions .get () // support async functions (block actions)
196+ actions .get ()
197+ await actions .increment (1 ) // + 1
198+ await actions .increment (1 ) // + 2
199+ await actions .increment (1 ) // + 3 as expected !
200+ return { light: ! state .light }
201+ },
202+ get : async () => {
203+ await new Promise ((resolve , reject ) =>
204+ setTimeout (() => {
205+ resolve ()
206+ }, 3000 )
207+ )
208+ return {
209+ response: {
210+ code: 200 ,
211+ message: ` ${new Date ().toLocaleString ()} open light success `
212+ }
213+ }
214+ }
215+ },
216+ state: initialState
217+ }
218+
219+ export default Model (model )
148220
149221// You can use these types when use Class Components.
150222// type ConsumerActionsType = getConsumerActionsType<typeof Model.actions>
@@ -157,7 +229,7 @@ export default Model
157229
158230### Model Register
159231
160- react-model keep the state and actions in a separate store. So you need to register them before using .
232+ react-model keep the state and actions in the separate private store. So you need to register them if you want to use them as the public models .
161233
162234` model/index.ts `
163235
@@ -166,9 +238,9 @@ import { Model } from 'react-model'
166238import Home from ' ../model/home'
167239import Shared from ' ../model/shared'
168240
169- const stores = { Home , Shared }
241+ const models = { Home , Shared }
170242
171- export const { getInitialState, useStore, getState, getActions , subscribe, unsubscribe } = Model (stores )
243+ export const { getInitialState, useStore, getState, actions , subscribe, unsubscribe } = Model (models )
172244```
173245
174246[ ⇧ back to top] ( #table-of-contents )
@@ -180,7 +252,7 @@ The actions return from useStore can invoke the dom changes.
180252
181253The execution of actions returned by useStore will invoke the rerender of current component first.
182254
183- It's the only difference between the actions returned by useStore and getActions now.
255+ It's the only difference between the actions returned by useStore and actions now.
184256
185257``` tsx
186258import React from ' react'
@@ -251,9 +323,9 @@ const BasicHook = () => {
251323
252324### actions
253325
254- You can call other models' actions with getActions api
326+ You can call other models' actions with actions api
255327
256- getActions can be used in both class components and functional components.
328+ actions can be used in both class components and functional components.
257329
258330``` js
259331import { actions } from ' ./index'
@@ -271,6 +343,8 @@ const model = {
271343export default model
272344```
273345
346+ [ ⇧ back to top] ( #table-of-contents )
347+
274348### subscribe
275349
276350subscribe(storeName, actions, callback) run the callback when the specific actions executed.
@@ -307,7 +381,7 @@ TypeScript Example
307381// StateType and ActionsParamType definition
308382// ...
309383
310- const Model : ModelType <StateType , ActionsParamType > = {
384+ const model : NextModelType <StateType , ActionsParamType > = {
311385 actions: {
312386 increment : async (s , _ , params ) => {
313387 // issue: https://github.com/Microsoft/TypeScript/issues/29196
@@ -321,6 +395,8 @@ const Model: ModelType<StateType, ActionsParamType> = {
321395 }
322396 }
323397}
398+
399+ export default Model (model )
324400```
325401
326402JavaScript Example
@@ -350,9 +426,9 @@ const initialState = {
350426 counter: 0
351427}
352428
353- const Model : ModelType <StateType , ActionsParamType > = {
429+ const model : NextModelType <StateType , ActionsParamType > = {
354430 actions: {
355- increment : (state , _ , params ) => {
431+ increment : (params , { state } ) => {
356432 return {
357433 counter: state .counter + (params || 1 )
358434 }
@@ -365,6 +441,8 @@ const Model: ModelType<StateType, ActionsParamType> = {
365441 },
366442 state: initialState
367443}
444+
445+ export default Model (model )
368446```
369447
370448</p >
@@ -674,3 +752,102 @@ Model({ Example }, JSON.parse(localStorage.getItem('__REACT_MODEL__')))
674752` ` `
675753
676754[⇧ back to top](#table-of-contents)
755+
756+ ### How can I deal with local state
757+
758+ What should I do to make every Counter hold there own model? 🤔
759+
760+ ` ` ` tsx
761+ class App extends Component {
762+ render() {
763+ return (
764+ <div className = " App" >
765+ <Counter />
766+ <Counter />
767+ <Counter />
768+ </div >
769+ )
770+ }
771+ }
772+ ` ` `
773+
774+ <details>
775+ <summary>Counter model</summary>
776+ <p>
777+
778+ ` ` ` ts
779+ interface State {
780+ count : number
781+ }
782+
783+ interface ActionParams {
784+ increment : number
785+ }
786+
787+ const model : NextModelType <State , ActionParams > = {
788+ state: {
789+ count: 0
790+ },
791+ actions: {
792+ increment : payload => {
793+ // 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
794+ // Not allowed
795+ // return state => (state.count += payload)
796+ return state => {
797+ state .count += payload
798+ }
799+ }
800+ }
801+ }
802+
803+ ` ` `
804+
805+ </p>
806+ </details>
807+
808+ <details>
809+ <summary>Counter.tsx</summary>
810+ <p>
811+
812+ ` ` ` tsx
813+
814+ const Counter = () => {
815+ const [{ useStore }] = useState (() => Model (model ))
816+ const [state, actions] = useStore ()
817+ return (
818+ <div >
819+ <div >{ state .count } </div >
820+ <button onClick = { () => actions .increment (3 )} >Increment</button >
821+ </div >
822+ )
823+ }
824+
825+ export default Counter
826+ ` ` `
827+
828+ </p>
829+ </details>
830+
831+ [⇧ back to top](#table-of-contents)
832+
833+ ### actions throw error from immer.module.js
834+
835+ ` ` `
836+ 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
837+ ` ` `
838+
839+ How to fix:
840+
841+ ` ` ` tsx
842+ actions : {
843+ increment: payload => {
844+ // Not allowed
845+ // return state => (state.count += payload)
846+ return state = > {
847+ state.count += payload
848+ }
849+ }
850+ }
851+ ` ` `
852+
853+ [⇧ back to top](#table-of-contents)
0 commit comments