@airma/react-state
Advanced tools
Comparing version 15.1.0 to 15.2.0
@@ -6,11 +6,18 @@ // src/index.ts | ||
import { useEffect, useRef, useState } from "react"; | ||
function useTupleModel(model, state) { | ||
function useTupleModel(model, state, onChange) { | ||
const modelRef = useRef(model); | ||
const ref = useRef(createModel(model, state)); | ||
const [s, setS] = useState(state); | ||
if (modelRef.current !== model) { | ||
if (modelRef.current !== model && onChange == null) { | ||
modelRef.current = model; | ||
ref.current.update(model); | ||
} | ||
if (onChange) { | ||
ref.current.update(model, { state }); | ||
} | ||
ref.current.connect(({ state: actionState }) => { | ||
if (onChange) { | ||
onChange(actionState); | ||
return; | ||
} | ||
setS(actionState); | ||
@@ -24,11 +31,16 @@ }); | ||
); | ||
return [ref.current.agent, s]; | ||
return [s, ref.current.agent]; | ||
} | ||
function useModel(model, state) { | ||
const [agent] = useTupleModel(model, state); | ||
const [, agent] = useTupleModel(model, state); | ||
return agent; | ||
} | ||
function useUncontrolledModel(model, state, onChange) { | ||
const [, agent] = useTupleModel(model, state, onChange); | ||
return agent; | ||
} | ||
export { | ||
useModel, | ||
useTupleModel | ||
useTupleModel, | ||
useUncontrolledModel | ||
}; |
@@ -1,11 +0,18 @@ | ||
import {AirModelInstance, AirReducer} from "@airma/core"; | ||
import { AirModelInstance, AirReducer } from '@airma/core'; | ||
export declare function useModel<S, T extends AirModelInstance,D extends S>( | ||
model: AirReducer<S, T>, | ||
state: D | ||
export declare function useModel<S, T extends AirModelInstance, D extends S>( | ||
model: AirReducer<S, T>, | ||
state: D | ||
): T; | ||
export declare function useTupleModel<S, T extends AirModelInstance,D extends S>( | ||
model: AirReducer<S, T>, | ||
state: D | ||
): [T, S]; | ||
export declare function useTupleModel< | ||
S, | ||
T extends AirModelInstance, | ||
D extends S | ||
>(model: AirReducer<S, T>, state: D): [S, T]; | ||
export function useUncontrolledModel< | ||
S, | ||
T extends AirModelInstance, | ||
D extends S | ||
>(model: AirReducer<S, T>, state: D, onChange: (s: S) => any): T; |
{ | ||
"name": "@airma/react-state", | ||
"version": "15.1.0", | ||
"version": "15.2.0", | ||
"module": "dist/index.js", | ||
@@ -38,3 +38,3 @@ "typings": "index.d.ts", | ||
"react-dom": ">=16.8.0", | ||
"@airma/core": "^15.1.0" | ||
"@airma/core": "^15.2.0" | ||
}, | ||
@@ -41,0 +41,0 @@ "devDependencies": { |
100
README.md
@@ -82,7 +82,5 @@ [![npm][npm-image]][npm-url] | ||
type FirstParam<A extends ((p:any)=>any)> = A extends (p:infer P)=>any? P : never; | ||
function useModel<S, T extends AirModelInstance>( | ||
function useModel<S, T extends AirModelInstance, D extends S>( | ||
model: AirReducer<S, T>, | ||
state: FirstParam<typeof model> | ||
state: D | ||
): T | ||
@@ -95,4 +93,5 @@ ``` | ||
* state - this is the default state for model initialization. | ||
* onChange - this is an optional callback, which can drive `useTupleModel` to a uncontrolled mode, and make state update by the param state change, you can see how to use it in `useUncontrolledModel` API. | ||
returns modelInstance and the current param state. | ||
returns the current param state and modelInstance, like `[state, instance]`. | ||
@@ -104,10 +103,91 @@ ```ts | ||
type FirstParam<A extends ((p:any)=>any)> = A extends (p:infer P)=>any? P : never; | ||
function useTupleModel<S, T extends AirModelInstance>( | ||
function useTupleModel<S, T extends AirModelInstance, D extends S>( | ||
model: AirReducer<S, T>, | ||
state: FirstParam<typeof model> | ||
): [T, S] | ||
state: D, | ||
onChange?:(s:S)=>any | ||
): [S, T] | ||
``` | ||
With this api, you can split state and methods like: | ||
```tsx | ||
const [count, {increase, decrease}] = useTupleModel((state:number)=>{ | ||
return { | ||
increase(){ | ||
return state + 1; | ||
}, | ||
decrease(){ | ||
return state - 1; | ||
} | ||
}; | ||
},0); | ||
``` | ||
### useUncontrolledModel | ||
* model - model generate function, it accepts a state param, and returns a model object, which contains methods for generating next state and any other properties for describing state. | ||
* state - this is the state for model, model can only update this state by `onChange` callback. | ||
* onChange - this is a callback for updating state to an outside state management, like `useState` API. | ||
```ts | ||
type AirModelInstance = Record<string, any>; | ||
type AirReducer<S, T extends AirModelInstance> = (state:S)=>T; | ||
function useUncontrolledModel< | ||
S, | ||
T extends AirModelInstance, | ||
D extends S | ||
>(model: AirReducer<S, T>, state: D, onChange: (s: S) => any): T | ||
``` | ||
With this API, you can use your model function more free, and more reusable. | ||
```tsx | ||
// model.ts | ||
export const counter = (count:number)=>{ | ||
return { | ||
count, | ||
increase(){ | ||
return count + 1; | ||
}, | ||
decrease(){ | ||
return count - 1; | ||
} | ||
}; | ||
}; | ||
//...... | ||
// component.ts | ||
import {useUncontrolledModel} from '@airma/react-state'; | ||
import {counter} from './model'; | ||
const MyComp = ({ | ||
value, | ||
onChange | ||
}:{ | ||
value:number, | ||
onChange:(v:number)=>void | ||
})=>{ | ||
const { | ||
count, | ||
increase, | ||
decrease | ||
} = useUncontrolledModel(counter, value, onChange); | ||
return ...... | ||
} | ||
function App(){ | ||
const [value, setValue] = useState<number>(0); | ||
return ( | ||
<div> | ||
<MyComp value={value} onChange={setValue}/> | ||
<div>{value}</div> | ||
</div> | ||
); | ||
} | ||
``` | ||
## Tips | ||
@@ -114,0 +194,0 @@ |
10098
59
242
Updated@airma/core@^15.2.0