Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
  • Loading branch information
zhangfisher committed Jan 19, 2024
1 parent 4cc7468 commit a6fc19b
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 106 deletions.
19 changes: 12 additions & 7 deletions example/src/FormDemo.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { MouseEvent } from "react";
import React, { useCallback } from "react";
import Card from "./components/Card"
import Network from './forms/network';
import JsonViewer from "./components/JsonViewer"
Expand All @@ -8,6 +8,7 @@ import { ReactFC } from "./types";
import ColorBlock from "./components/ColorBlock";
import { Loading } from "./components/Loading";
import { Button } from "./components/Button";
import { ActionRunOptions } from '../../packages/form/src/action';

const FieldRow:ReactFC<{label?:string,visible?:boolean,enable?:boolean}> = ({enable,visible,label,children})=>{
return (
Expand Down Expand Up @@ -176,9 +177,14 @@ const FormDemo:React.FC = ()=>{
// 如果缺少以下两句,则state.select无法触发setOnReadHook
const [state] = Network.useState()

// Network.actions.submit().then(()=>{
// console.log('submit success')
// })
const submit = useCallback((options?:ActionRunOptions)=>{
Network.actions.submit(options).then((result)=>{
console.log("提交结果:",result)
}).catch((error)=>{
console.log("提交错误:",error)
})
},[])

// Network.state.actions.ping.execute

// state.dhcp.start.validate.value
Expand All @@ -188,9 +194,8 @@ const FormDemo:React.FC = ()=>{
<div style={{padding:"8px",margin:'8px',width:'60%'}}>
<NetworkForm/>
{/* <NetworkForm/> */}
<button onClick={()=>Network.actions.submit()}>提交</button>
<button onClick={()=>Network.actions.errorSubmit()}>提交错误</button>
<button onClick={()=>Network.actions.timeoutSubmit()}>提交超时</button>
<button onClick={()=>submit()}>提交</button>
<button onClick={()=>submit({timeout:1000})}>提交超时</button>
</div>
<div style={{padding:"8px",margin:'8px',width:'40%'}}>
<Card title="表单数据">
Expand Down
22 changes: 12 additions & 10 deletions example/src/forms/network.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const formSchema = {
value: "React-Helux-Form",
placeholder: "输入网络配置名称",
title: "网络名称",
validate:computed<boolean>(async (value: string) => value.length > 3,[])
validate:computed<boolean>(async (value: string) => value.length > 3)
},
title: {
value: "React-Helux-Form",
Expand Down Expand Up @@ -54,8 +54,9 @@ const formSchema = {
(dhcp: any) => {
return dhcp.enable.value;
},
["dhcp.enable.value"],
{ scope: ComputedScopeRef.Parent }
{
scope: ComputedScopeRef.Parent
}
),
validate: (value: any) => validator.isIP(value),
},
Expand All @@ -66,9 +67,11 @@ const formSchema = {
visible: computed<boolean>(
(fields: any) => {
return fields.dhcp.enable.value;
},
["dhcp.enable.value"],
{ scope: ComputedScopeRef.Root }
},
{
scope: ComputedScopeRef.Root ,
depends:["dhcp.enable.value"]
}
),
validate: (value: any) => validator.isIP(value),
},
Expand Down Expand Up @@ -102,7 +105,7 @@ const formSchema = {
execute:computed(async (wifi:any)=>{
await delay(6000)
console.log("提交wifi=",wifi)
},[],{
},{
timeout:[5 * 1000,5]
})
},
Expand Down Expand Up @@ -130,9 +133,8 @@ const formSchema = {
enable: (root: any) => {
return root.fields.wifi.ssid.value.length > 3
},
execute: async (fields: any) => {
execute: async () => {
await delay(2000);
console.log(fields);
return count++
},
},
Expand All @@ -147,7 +149,7 @@ const formSchema = {
title: "提交超时",
execute: computed(async () => {
await delay(5000);
},[],{timeout:2000}),
},{timeout:2000}),
},
ping: {
title: "测试网络连通性",
Expand Down
41 changes: 21 additions & 20 deletions packages/form/src/action.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import { Dict, HttpFormEnctype, HttpMethod } from "./types";
import { getVal } from "@helux/utils";
import React from "react";
import type { FormOptions } from "./form";
import { AsyncComputedObject, ComputedAsyncReturns, ComputedDepends, ComputedOptions, ComputedParams, IStore, computed, getValueByPath } from 'helux-store';
import { AsyncComputedObject, ComputedAsyncReturns, ComputedDepends, ComputedOptions, ComputedParams, IStore, computed, getValueByPath, watch } from 'helux-store';
import { debounce } from './utils';
import { timeout as timeoutWrapper } from "flex-tools/func/timeout";
import { noReentry } from "flex-tools/func/noReentry";
Expand Down Expand Up @@ -116,22 +116,23 @@ export type FormActionState = {
execute:AsyncComputedObject
}

function createFormAction<Scope extends Dict = Dict,Result=any>(name:string,setState:any){
return async (options?:ActionRunOptions)=>{
// action.execute依赖于scope和count两个属性,当变化时会触发重新执行
// 由于action.execute依赖于count,所以当count++时会触发动作执行
const opts = Object.assign({timeout:0,debounce:0,noReentry:false},options)
let fn = async ()=> setState((state:any)=>state.actions[name].count++)
if(opts.noReentry){
fn = noReentry(fn)
}
function createFormAction(name:string,store:IStore){
const actionFn = async ()=>{
const computedObject = store.state.actions[name]
const [snap] = await computedObject.execute.run()
return getVal(snap,['actions',name,'execute','value'])
}
return async (options?:ActionRunOptions)=>{
let fn = actionFn
const opts = Object.assign({
noReentry:false,
timeout:0,
debounce:0
},options)
if(opts.timeout>0){
fn = timeoutWrapper(fn,{value:opts.timeout})
}
if(opts.debounce>0){
fn = debounce(fn,opts.debounce)
}
return await fn()
}
return await fn()
}
}

Expand All @@ -143,12 +144,12 @@ function createFormAction<Scope extends Dict = Dict,Result=any>(name:string,set
* @param actionStates 经过helux-store计算后的动作声明
* @returns
*/
export function createFormActions<ActionStates extends Dict>(this:IStore,actionExecutors:ActionStates){
const store = this
export function createFormActions<ActionStates extends Dict>(this:IStore){
const actions:Dict={}
// Object.entries(actionExecutors).forEach(([name,actionExecutor])=>{
// actions[name]= createFormAction.call(store,name,store.setState)
// })
const store = this
Object.keys(store.state.actions).forEach((name)=>{
actions[name]= createFormAction(name,this)
})
return actions as ActionRecords<ActionStates>
}

Expand Down
38 changes: 4 additions & 34 deletions packages/form/src/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ import { FieldComponent, createFieldComponent } from './field';
import { FieldGroupComponent, createFieldGroupComponent } from "./fieldGroup";
import { assignObject } from "flex-tools/object/assignObject";
import { ActionRecords, FormActions, createActionComponent, createFormActions } from './action';
import { ACTIONS_STATE_KEY, FIELDS_STATE_KEY } from "./consts";
import { FIELDS_STATE_KEY } from "./consts";
import { defaultObject } from "flex-tools/object/defaultObject";


Expand Down Expand Up @@ -212,35 +212,7 @@ function setFormDefault(define:any){
function loadFormData(store:any){

}

function filterFormActions<Schema extends Dict=Dict>(define: Schema): Record<string, Function> {
// return Object.entries(define.actions || {}).reduce((actions: Record<string, Function>, [name, action]: [string, any]) => {
// const executor = action.execute
// actions[name] = executor;
// const actionDeps = [
// [ACTIONS_STATE_KEY,name,"count"],
// [ACTIONS_STATE_KEY,name,"scope"]
// ]
// // 使用原始的async方式声明动作
// if(action.execute.__COMPUTED__ == undefined){
// action.execute = computed(executor,actionDeps,
// {
// // 动作的上下文总是指向scope所描述的路径
// // 例: scope="wifi",则动作的上下文总是指向state.actions.wifi,将作为第一个参数传入execute函数
// scope:"@scope",
// toComputedResult:'current'
// })
// }else{ // 使用computed方式声明动作
// const executorParms = action.execute as AsyncComputedParams<any>
// executorParms.options.scope = "@scope"
// executorParms.options.toComputedResult = 'current'
// if(!Array.isArray(executorParms.options.depends)) executorParms.options.depends = []
// executorParms.options.depends.push(...actionDeps)
// }
// return actions;
// }, {});
}


/**
* 我们约定,每一个动作均由一个{execute:computed(async ()=>{})}对象来描述
*
Expand Down Expand Up @@ -279,9 +251,7 @@ export function createForm<Schema extends Dict=Dict>(define: Schema,options?:For
},options) as Required<FormOptions<Schema>>

// 注入表单默认属性
setFormDefault(define)
// 提取所有动作的execute函数,用来创建表单动作对象计算函数
const actionExecutors = filterFormActions(define);
setFormDefault(define)

// 创建表单Store对象实例
const store = createStore<StoreSchema<Schema>>({state:define},{
Expand Down Expand Up @@ -309,7 +279,7 @@ export function createForm<Schema extends Dict=Dict>(define: Schema,options?:For
type StoreType = typeof store
type FieldsType = (typeof store.state)['fields']
type ActionsType = (typeof store.state)['actions']
const actions = createFormActions.call<IStore,[ActionsType],ActionRecords<Schema['actions']>>(store as unknown as IStore,actionExecutors)
const actions = createFormActions.call<IStore,[],ActionRecords<Schema['actions']>>(store as unknown as IStore)
return {
Form: createFormComponent.call<FormOptions,any[],FormComponent<Schema>>(opts,store),
Field: createFieldComponent.call(opts,store),
Expand Down
56 changes: 24 additions & 32 deletions packages/store/src/computed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@ import type { StoreSchema, ComputedScope, StoreOptions, ComputedContext, IStore
import { ComputedScopeRef } from "./store";
import { getVal, setVal } from "@helux/utils";
import { isAsyncFunction } from "flex-tools/typecheck/isAsyncFunction";
import { isPlainObject } from "flex-tools/typecheck/isPlainObject";
import { skipComputed, isSkipComputed, getValueByPath, joinValuePath } from "./utils";
import { switchValue } from "flex-tools/misc/switchValue";
import { assignObject } from "flex-tools/object/assignObject";
import { Dict } from "./types";


export interface ComputedProgressbar{
value:(num:number)=>void
end:()=>void
Expand Down Expand Up @@ -74,8 +75,8 @@ export interface ComputedOptions<Value=any,Extras extends Dict={}> {
};

export type ComputedDepends = Array<string> | Array<string[]> | Array<string | Array<string>> | ((draft: any) => any[])
export type ComputedGetter<R> = (scopeDraft: any) => Exclude<R,Promise<any>>
export type AsyncComputedGetter<R> = (scopeDraft:any,options:Required<ComputedParams>) => Promise<R>
export type ComputedGetter<R,Scope extends Dict=Dict> = (scopeDraft: Scope) => Exclude<R,Promise<any>>
export type AsyncComputedGetter<R,Scope extends Dict=Dict> = (scopeDraft:Scope,options:Required<ComputedParams>) => Promise<R>

export type AsyncComputedObject<Value= any,ExtAttrs extends Dict = {}> ={
loading? : boolean;
Expand Down Expand Up @@ -173,36 +174,27 @@ function getComputedRefDraft(draft: any, params:{input:any[],type:'context' | 's
* @returns
*
*/
export function computed<R = any,ExtraAttrs extends Dict = {}>( getter: AsyncComputedGetter<R>, depends: ComputedDepends, options?: ComputedOptions<R,ExtraAttrs>): ComputedAsyncReturns<R & ExtraAttrs>;
export function computed<R = any,ExtraAttrs extends Dict = {}>( getter: ComputedGetter<R>, options?: ComputedOptions<R,ExtraAttrs>): R
export function computed<R = any,ExtraAttrs extends Dict = {}>( getter: any,depends: any, options?: ComputedOptions<R,ExtraAttrs>): ComputedAsyncReturns<R & ExtraAttrs> {
export function computed<R = any,Scope extends Dict=Dict,ExtraAttrs extends Dict = {}>( getter: AsyncComputedGetter<R>,options?: ComputedOptions<R,ExtraAttrs>): ComputedAsyncReturns<R & ExtraAttrs>;
export function computed<R = any,Scope extends Dict=Dict,ExtraAttrs extends Dict = {}>( getter: ComputedGetter<R>, options?: ComputedOptions<R,ExtraAttrs>): R
export function computed<R = any,Scope extends Dict=Dict,ExtraAttrs extends Dict = {}>( getter: any, options?: ComputedOptions<R,ExtraAttrs>): ComputedAsyncReturns<R & ExtraAttrs> {

if (typeof getter != "function") throw new Error("getter must be a function");
const opts: ComputedOptions<R> = {
const opts: Required<ComputedOptions<R,ExtraAttrs>> = assignObject({
async: false,
timeout:0,
toComputedResult:'self',
depends: [],
scope:"current",
toComputedResult:'self',
immediate:true,
};
},options)

// 是否是异步计算函数
const isAsync = opts.async === true
|| isAsyncFunction(getter)
|| (arguments.length == 2 && Array.isArray(arguments[1]))
|| (arguments.length == 3 && Array.isArray(arguments[1]) && isPlainObject(arguments[2]));
const isAsync = opts.async === true || isAsyncFunction(getter)

if (isAsync) {
opts.depends = arguments[1] || [];
Object.assign(opts, {
scope: ComputedScopeRef.Current, // 异步计算函数的上下文指向依赖
},arguments[2] || {}
);
} else {
opts.depends = []; // 同步计算函数不需要指定依赖
Object.assign(opts,
{ scope: ComputedScopeRef.Current }, // 异步计算函数的上下文指向依赖
arguments[1] || {}
);
if(Array.isArray(opts.depends) && opts.depends.length==0){
console.warn("async computed function should specify depends")
}
}

opts.async = isAsync;
Expand Down Expand Up @@ -323,7 +315,7 @@ function createAsyncComputedObject(stateCtx:any,mutateDesc:string,valueObj:Parti
progress: 0,
run: markRaw(
skipComputed(() => {
stateCtx.runMutateTask(mutateDesc);
return stateCtx.runMutateTask(mutateDesc);
})
)
},valueObj)
Expand Down Expand Up @@ -389,7 +381,6 @@ async function executeComputedGetter<R>(draft:any,getter:AsyncComputedGetter<R>,
}

let timerId:any,countdownId:any,hasError=false,isTimeout=false

const afterUpdated={} // 保存执行完成后需要更新的内容,以便在最后一起更新
try {
// 处理超时参数和倒计时
Expand Down Expand Up @@ -432,7 +423,7 @@ async function executeComputedGetter<R>(draft:any,getter:AsyncComputedGetter<R>,
Object.assign(afterUpdated,{loading:false})
if(!hasError && !isTimeout) Object.assign(afterUpdated,{error:null})
updateAsyncComputedObject(setState,computedResultPath,afterUpdated)
}
}
}

/**
Expand Down Expand Up @@ -494,14 +485,13 @@ function createAsyncComputedMutate<Store extends StoreSchema<any>>(stateCtx: ISh
},
// 此函数在依赖变化时执行,用来异步计算
task: async ({ draft, setState, input }) => {
await executeComputedGetter(draft,getter,{computedResultPath,input,computedOptions,computedContext,storeOptions,setState})
return await executeComputedGetter(draft,getter,{computedResultPath,input,computedOptions,computedContext,storeOptions,setState})
},
immediate,
desc:mutateId,
checkDeadCycle: false,
});


return computeObjects[mutateId]
}


Expand All @@ -524,7 +514,8 @@ export function createComputed<Store extends StoreSchema<any>>(stateCtx: IShared
// 将声明在state里面的计算函数转换为helux的mutate
//******** 使用computed创建 ****************** */
if (value.__COMPUTED__=='async') {
createAsyncComputedMutate<Store>(stateCtx, params,computeObjects, options);
const mutate = createAsyncComputedMutate<Store>(stateCtx, params,computeObjects, options);
if(mutate) params.replaceValue(getVal((mutate as any).snap,valuePath));
} else if (isAsyncFunction(value)) {
// 简单的异步计算函数,没有通过computed函数创建,此时由于没有指定依赖,所以只会执行一次
params.value = () => ({
Expand All @@ -536,7 +527,8 @@ export function createComputed<Store extends StoreSchema<any>>(stateCtx: IShared
context: options.computedThis, // 指定默认上下文
},
});
createAsyncComputedMutate<Store>(stateCtx, params,computeObjects, options);
const mutate = createAsyncComputedMutate<Store>(stateCtx, params,computeObjects, options);
if(mutate) params.replaceValue(getVal((mutate as any).snap,valuePath));
} else {
// 直接声明同步计算函数,使用全局配置的计算上下文
createComputedMutate<Store>(stateCtx, params, computeObjects, {},options);
Expand Down
Loading

0 comments on commit a6fc19b

Please sign in to comment.