Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
  • Loading branch information
zhangfisher committed Aug 24, 2024
1 parent 6369102 commit 5a9584a
Show file tree
Hide file tree
Showing 18 changed files with 156 additions and 166 deletions.
10 changes: 6 additions & 4 deletions example/src/forms/network/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -194,8 +194,7 @@ const Network = createForm({
enable: (root: any) => {
return root.fields.wifi.ssid.value.length > 3
},
execute: action(async (scope:any,{abortSignal}) => {
console.log(scope)
execute: action(async (_:any,{abortSignal}) => {
return new Promise<number>((resolve,reject)=>{
setTimeout(()=>{
resolve(count++)
Expand All @@ -221,15 +220,18 @@ const Network = createForm({
},
ping: {
title: "测试网络连通性",
scope: "wifi", // 表示该动作的上下文是wifi这个子表单
enable: (wifi: any) => wifi.ssid.value.length > 0,
scope:"fields.wifi",
enable: computed((wifi: any) => {
return wifi.ssid.value.length > 0
},{scope:"fields.wifi"}),
execute: async (a: Dict) => {
await delay(2000);
console.log(a);
},
},
// 向导表单:上一步
previous:{

enable: (wifi: any) => wifi.ssid.value.length > 0,
execute:async ()=>{
return 1
Expand Down
11 changes: 10 additions & 1 deletion example/src/forms/network/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import {Card,JsonViewer, Button,Divider,Field,Input } from "@speedform/demo-com


const NetworkForm = ()=>{
Network.state.fields.wifi.password.value
// @ts-ignore
globalThis.Network = Network
return <Network.Form className="panel">
<div data-loader="circle"></div>
<Card title="网络配置">
Expand Down Expand Up @@ -112,6 +113,14 @@ const NetworkForm = ()=>{
</>
}}
</Network.Action>
<Network.Action<typeof Network.actions.ping> name="ping" >
{({title,visible,loading,enable,run,error,progress})=>{
return <>
enable= {String(enable)}
<Button disabled={!enable} loading={loading} progress={progress} visible={visible} enable={enable} error={error} onClick={run()}>{title}</Button>
</>
}}
</Network.Action>
<Network.Action<typeof Network.fields.wifi.cancelableSubmit> name="fields.wifi.cancelableSubmit" >
{({title,visible,loading,enable,run,cancel,error,progress})=>{
return <>
Expand Down
44 changes: 16 additions & 28 deletions packages/core/src/action.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@

import { ReactNode, useCallback, useRef, RefObject,useState} from "react";
import React from "react";
import type { FormDefine, FormStore, RequiredFormOptions } from "./form";
import type { FormDefine, FormStore } from "./form";
import { AsyncComputedDefine, AsyncComputedGetter, AsyncComputedObject, ComputedDescriptorDefine, ComputedOptions, ComputedParams, Dict, RuntimeComputedOptions, computed, getValueByPath} from '@speedform/reactive';
import { omit } from "flex-tools/object/omit";
import { getFormData } from "./serialize";
Expand Down Expand Up @@ -148,6 +148,8 @@ export type ActionProps<State extends FormActionState=FormActionState,Params ext
* run({..计算函数的配置参数,覆盖上面的配置...
* abortable?:boolean // 是否可取消,如果可取消
* abortController?:AbortController // 传入一个AbortController用来传递给动作计算函数
* abortSignal?:AbortSignal // 传入一个AbortSignal用来传递给动作计算函数
* extras?:any // 传递给计算函数的额外参数
* })
*
*
Expand Down Expand Up @@ -242,6 +244,9 @@ export const ActionChildren = React.memo((props: {actionProps:ActionRenderProps<
*
* 用于创建动作组件,动作组件用于提交表单,重置表单,校验表单以及自定义等操作
*
* <Action name="指向声明动作的路径">
*
*
* @param this
* @param store
* @returns
Expand All @@ -260,10 +265,8 @@ export function createActionComponent<State extends Dict = Dict>(store:FormStore
const [state] = store.useState()
let { name:actionKey } = props
// 如果动作是声明在actions里面可以省略actions前缀
if(!actionKey.includes(".")) actionKey = `actions.${actionKey}`

const actionState = getValueByPath(state,actionKey,".")

if(!actionKey.includes(".")) actionKey = `actions.${actionKey}`
const actionState = getValueByPath(state,actionKey)
if(actionState==null){
store.options.log(`Action ${actionKey} is not defined`,"error")
return <>{props.children}</>
Expand All @@ -273,10 +276,8 @@ export function createActionComponent<State extends Dict = Dict>(store:FormStore
const actionCanceller = useActionCanceller(state,actionKey)
// 用来引用当前动作
const ref = useRef<HTMLElement>(null)

// 创建动作组件的Props
const actionRenderProps = createActionRenderProps(actionState,actionRunner,actionCanceller,ref)

// 执行渲染动作组件
if(typeof(props.render)==='function'){
return <ActionChildren {...{actionProps:actionRenderProps,children:props.render}} />
Expand All @@ -288,6 +289,8 @@ export function createActionComponent<State extends Dict = Dict>(store:FormStore
}else{
return <ActionChildren {...{actionProps:actionRenderProps,children:props.children}} />
}
}else{
return <></>
}
}
return React.memo(Action,(oldProps:any, newProps:any)=>{
Expand All @@ -302,6 +305,7 @@ export function createActionComponent<State extends Dict = Dict>(store:FormStore
* 该函数实现以下功能:
* - 从store中获取动作的状态数据传递给Action的getter函数 *
*
*
* @param getter
* @param options
*/
Expand All @@ -311,32 +315,16 @@ export function action<Values extends Dict=Dict,R=any>(getter: AsyncComputedGett
// 比如 getSnap(state.fields.xxx.xxx)也是返回整个state的快照
const data = getFormData(Object.assign({},scope))
return await (getter as unknown as AsyncComputedGetter<R>)(data,opts)
},[],Object.assign({},options,{async:true}))
},[
// 不依赖于任意表单数据,需要手动触发执行
],Object.assign({},options,{
async:true
}))
}




export type SubmitAsyncComputedGetter<R> = AsyncComputedGetter<R,FormData>
export type SubmitActionOptions<R> = ComputedOptions<R>

/**
*
* 特殊的对象传入一个FormData对象
* 声明一个提交动作
* submit动作总是返回一个FormData对象
*
* @param getter
* @param options
* @returns
*/
export function submit<R=any>(getter: SubmitAsyncComputedGetter<R>,options?: SubmitActionOptions<R>){
return action<Dict,R>(async (data:Dict,opts)=>{
const formData = new FormData()
return await (getter as unknown as SubmitAsyncComputedGetter<R>)(formData,opts)
},options)

}


export type UseActionType = <Scope extends Dict=Dict,R=any>(executor:AsyncComputedGetter<R,Scope>,options?:ComputedOptions<R> & {name?:string})=>FormActionState['execute']
Expand Down
21 changes: 16 additions & 5 deletions packages/core/src/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -227,10 +227,20 @@ function setFormDefault<T extends Dict>(define:T){
* - immediate=false : 不会自动执行,需要手动调用action.execute.run()来执行
* - 让scope默认指向fields,这样就可以直接使用fields下的字段,而不需要fields前缀
*
* actions:{
* ping:{
* scope:"wifi", // 当指定scope时,execute.scope由scope指定
* execute:computed(async (wifi: any) => {
* // ....
* })
* }
* }
*
*/
function createActionHook(valuePath:string[],options:ComputedOptions){
if(valuePath.length>1 && valuePath[valuePath.length-1]=='execute'){
options.immediate = false // 默认不自动执行,需要手动调用action.execute.run()来执行
// 默认不自动执行,需要手动调用action.execute.run()来执行
options.immediate = false
// 如果没有指定scope,则默认指向fields,这样就可以直接使用fields下的字段,而不需要fields前缀
if(options.scope){
if(Array.isArray(options.scope)){
Expand All @@ -239,8 +249,8 @@ function createActionHook(valuePath:string[],options:ComputedOptions){
options.scope.unshift(FIELDS_STATE_KEY)
}
}
}else{
options.scope = [FIELDS_STATE_KEY]
}else{// 如果没有指定scope,则默认指向fields,
options.scope = [FIELDS_STATE_KEY]
}
options.noReentry = true // 禁止重入
}
Expand Down Expand Up @@ -313,13 +323,14 @@ export function createForm<State extends FormDefine=FormDefine>(schema: State,op
createActionHook(valuePath,options)
},
onComputedDraft(draft,{computedType,valuePath}){
// 针对计属性
// 针对计算属性
// 修改fields下的所有计算函数的作用域根,使之总是指向fields开头
// 这样可以保证在计算函数中,当scope->Root时,总是指向fields,否则就需要state.fields.xxx.xxx
if(computedType==='Computed' && valuePath.length >0 && valuePath[0]==FIELDS_STATE_KEY){
return draft.fields
}
}
},
immediate:true // 默认立即执行完成所有计算属性的初始化
});

/**
Expand Down
27 changes: 25 additions & 2 deletions packages/core/src/submit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
*
*/
import React, { useCallback, useRef } from 'react'
import { Dict, getValueByPath } from "@speedform/reactive";
import { AsyncComputedGetter, ComputedOptions, Dict, getValueByPath } from "@speedform/reactive";
import type { FormSchemaBase, FormStore, RequiredFormOptions } from "./form";
import { CSSProperties, ReactElement, ReactNode } from "react";
import { isFieldGroup, isFieldList, isFieldValue } from "./utils";
Expand Down Expand Up @@ -244,4 +244,27 @@ export const $submit = {
console.log("scope=",scope,"options=",options)
debugger
})
}
}


export type SubmitAsyncComputedGetter<R> = AsyncComputedGetter<R,FormData>
export type SubmitActionOptions<R> = ComputedOptions<R>


/**
*
* 特殊的对象传入一个FormData对象
* 声明一个提交动作
* submit动作总是返回一个FormData对象
*
* @param getter
* @param options
* @returns
*/
export function submit<R=any>(getter: SubmitAsyncComputedGetter<R>,options?: SubmitActionOptions<R>){
return action<Dict,R>(async (data:Dict,opts)=>{
const formData = new FormData()
return await (getter as unknown as SubmitAsyncComputedGetter<R>)(formData,opts)
},options)

}
52 changes: 0 additions & 52 deletions packages/reactive/src/action.ts

This file was deleted.

23 changes: 11 additions & 12 deletions packages/reactive/src/computed/async.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import type { IStore } from "../store/types";
import { skipComputed, joinValuePath, getError, getDepValues,getVal, setVal, getComputedId } from "../utils";
import { delay } from 'flex-tools/async/delay';
import { OBJECT_PATH_DELIMITER } from '../consts';
import { getComputedScope } from '../context';
import { getComputedScope } from '../scope';
import { AsyncComputedObject, ComputedOptions, ComputedParams, ComputedProgressbar } from './types';
import type { ComputedDescriptorDefine, ComputedRunContext } from './types';
import { IReactiveReadHookParams } from '../reactives/types';
Expand Down Expand Up @@ -74,7 +74,6 @@ async function executeComputedGetter<T extends Dict>(draft:any,computedRunContex
const { timeout=0,retry=[0,0],selfReactiveable } = computedOptions
const setState = selfReactiveable ? selfReactiveable.setState.bind(selfReactiveable) : store.setState
//
const thisDraft = draft
const scopeDraft = getComputedScope(store,computedOptions,{draft,dependValues,valuePath,computedType:"Computed"} )

const [retryCount,retryInterval] = Array.isArray(retry) ? retry : [Number(retry),0]
Expand Down Expand Up @@ -135,7 +134,7 @@ async function executeComputedGetter<T extends Dict>(draft:any,computedRunContex
}
}
// 执行计算函数
computedResult = await getter.call(thisDraft, scopeDraft,computedParams);
computedResult = await getter.call(store, scopeDraft,computedParams);
if(hasAbort) throw new Error("Abort")
if(!hasTimeout){
Object.assign(afterUpdated,{result:computedResult,error:null,timeout:0})
Expand Down Expand Up @@ -179,7 +178,7 @@ async function executeComputedGetter<T extends Dict>(draft:any,computedRunContex



function createComputed<T extends Dict>(computedRunContext:ComputedRunContext,computedOptions:ComputedOptions,store:IStore<T>){
function createComputed<State extends Dict>(computedRunContext:ComputedRunContext,computedOptions:ComputedOptions,store:IStore<State>){
const { valuePath, id:computedId,deps,desc:computedDesc } = computedRunContext
const { selfReactiveable,initial,noReentry } = computedOptions

Expand Down Expand Up @@ -218,7 +217,7 @@ function createComputed<T extends Dict>(computedRunContext:ComputedRunContext,co
computedRunContext.isComputedRunning=true
computedRunContext.dependValues = values // 即所依赖项的值
try{
const r= await executeComputedGetter(draft,computedRunContext,finalComputedOptions,store)
const r= await executeComputedGetter<State>(draft,computedRunContext,finalComputedOptions,store)
return r
}finally{
computedRunContext.isComputedRunning=false
Expand All @@ -234,14 +233,14 @@ function createComputed<T extends Dict>(computedRunContext:ComputedRunContext,co
* @param stateCtx
* @param params
*/
export function createAsyncComputedMutate<T extends Dict,R=any>(computedParams:IReactiveReadHookParams,store:IStore<T>) :ComputedObject<T,AsyncComputedObject<R>> {
export function createAsyncComputedMutate<State extends Dict,R=any>(computedParams:IReactiveReadHookParams,store:IStore<State>) :ComputedObject<State,AsyncComputedObject<R>> | undefined {

// 1. 参数检查
const { path:valuePath, parent ,value } = computedParams;
// // 排除掉所有非own属性,例如valueOf等
// if (parent && !Object.hasOwn(parent, valuePath[valuePath.length - 1])) {
// return;
// }
if (parent && !Object.hasOwn(parent, valuePath[valuePath.length - 1])) {
return;
}

// 2. 获取到计算属性描述信息: 包括getter和配置。 此时value是一个函数
let { getter, options: computedOptions } = value() as ComputedDescriptorDefine<any>
Expand All @@ -264,7 +263,7 @@ export function createAsyncComputedMutate<T extends Dict,R=any>(computedParams:
// 计算对象的id和name,name用于打印日志时提供更多信息
const computedId = getComputedId(valuePath,computedOptions)
computedOptions.id = computedId
const computedDesc = `${computedId}_${valuePath.join(OBJECT_PATH_DELIMITER)}`
const computedDesc = valuePath.join(OBJECT_PATH_DELIMITER)

store.options.log(`Create async computed: ${computedDesc} (depends=${depends.length==0 ? 'None' : joinValuePath(depends)})`);

Expand All @@ -279,13 +278,13 @@ export function createAsyncComputedMutate<T extends Dict,R=any>(computedParams:
deps : depends,
getter
}
createComputed(computedRunContext,computedOptions,store)
createComputed<State>(computedRunContext,computedOptions,store)

// 移花接木原地替换
if(!selfReactiveable) computedParams.replaceValue(getVal(store.state, valuePath));

// 8. 创建计算对象实例
const computedObject = new ComputedObject<T,AsyncComputedObject<R>>(store,selfReactiveable,valuePath,computedOptions)
const computedObject = new ComputedObject<State,AsyncComputedObject<R>>(store,selfReactiveable,valuePath,computedOptions)
if(computedOptions.save) store.computedObjects.set(computedId,computedObject)
return computedObject
}
Expand Down
Loading

0 comments on commit 5a9584a

Please sign in to comment.