Skip to content

Commit f690495

Browse files
committed
feat: add middleware for tracking and improve field validation readability
1 parent 18b8a46 commit f690495

File tree

7 files changed

+99
-23
lines changed

7 files changed

+99
-23
lines changed

packages/ui/src/components/form/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export {
1212
useWatch
1313
} from 'skyroc-form';
1414

15-
export type { FormInstance } from 'skyroc-form';
15+
export type { Action as FormAction, FormInstance } from 'skyroc-form';
1616

1717
export { default as FormField } from './FormField';
1818

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
'use client';
2+
3+
import { useEffect } from 'react';
4+
import type { FormAction } from 'soybean-react-ui';
5+
import { Button, Card, Form, FormField, Input, useForm } from 'soybean-react-ui';
6+
7+
// ============ Analytics Middleware (logging/tracking) ============
8+
function analyticsMiddleware({ getState }: { dispatch: (a: FormAction) => void; getState: () => any }) {
9+
return (next: (a: FormAction) => void) => (action: FormAction) => {
10+
// the action before the middleware
11+
console.log('[middleware] before', action, 'state:', getState());
12+
13+
next(action); // run default logic first
14+
15+
// the action after the middleware
16+
console.log('[middleware] after', action, 'state:', getState());
17+
18+
if (action.type === 'setFieldValue') {
19+
console.log(`[Analytics] User modified field: ${action.name}`, action.value);
20+
// Report tracking event
21+
// report({ field: action.name, value: action.value });
22+
}
23+
};
24+
}
25+
26+
// ============ Form field types ============
27+
type Inputs = {
28+
confirmPassword: string;
29+
password: string;
30+
username: string;
31+
};
32+
33+
const initialValues: Inputs = {
34+
confirmPassword: '123456',
35+
password: '123456',
36+
username: 'ohh'
37+
};
38+
39+
const UseFormWithMiddleware = () => {
40+
const [form] = useForm<Inputs>();
41+
42+
useEffect(() => {
43+
// Register middleware: add analytics/tracing for form actions
44+
form.use(analyticsMiddleware);
45+
}, []);
46+
47+
return (
48+
<Card title="UseForm with Middleware (Sync Password)">
49+
<Form
50+
className="w-[480px] max-sm:w-full space-y-4"
51+
form={form}
52+
initialValues={initialValues}
53+
>
54+
<FormField
55+
label="Username"
56+
name="username"
57+
>
58+
<Input />
59+
</FormField>
60+
61+
<FormField
62+
label="Password"
63+
name="password"
64+
>
65+
<Input />
66+
</FormField>
67+
68+
<FormField
69+
label="Confirm Password"
70+
name="confirmPassword"
71+
>
72+
<Input />
73+
</FormField>
74+
75+
<div className="flex gap-2 flex-wrap">
76+
<Button type="submit">Submit</Button>
77+
<Button onClick={() => form.setFieldValue('username', 'ohh-889')}>Set Username</Button>
78+
</div>
79+
</Form>
80+
</Card>
81+
);
82+
};
83+
84+
export default UseFormWithMiddleware;

playground/src/app/(demo)/form/page.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import ComputedDemo from './modules/ComputedDemo';
33
import Default from './modules/Default';
44
import FieldChange from './modules/FieldChange';
55
import List from './modules/List';
6+
import Middleware from './modules/Middleware';
67
import Preserve from './modules/Preserve';
78
import Reset from './modules/Reset';
89
import UseForm from './modules/UseForm';
@@ -36,6 +37,8 @@ const FormPage = () => {
3637

3738
<Preserve />
3839

40+
<Middleware />
41+
3942
<ClearDestroy />
4043
</div>
4144
);

primitives/filed-form /src/form-core/createStore.ts

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,6 @@ class FormStore {
144144
>();
145145
/** Reverse index mapping dependency keys to computed fields */
146146
private _depIndex = new Map<string, Set<string>>();
147-
/** Queue for recomputation (reserved for future use) */
148-
private _recomputeQueue = new Set<string>();
149147

150148
// Field visibility management
151149
/** Set of disabled field keys */
@@ -266,12 +264,7 @@ class FormStore {
266264
/**
267265
* Enhanced dispatch function that processes actions through middleware chain
268266
*/
269-
private dispatch = (a: Action) => {
270-
const ctx = { dispatch: (x: Action) => this.dispatch(x), getState: () => this._store };
271-
const chain = this._middlewares.map(mw => mw(ctx));
272-
const reduced = chain.reduceRight((next, mw) => mw(next), this.baseDispatch);
273-
reduced(a);
274-
};
267+
private dispatch = (a: Action) => this.baseDispatch(a);
275268

276269
// ------------------------------------------------
277270
// Store and Initial Values Management
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
export type { AllPathsKeys, FieldElement } from 'skyroc-type-utils';
22

3+
export type { Action } from './form-core/middleware';
4+
35
export * from './react';

primitives/filed-form /src/react/components/Field.tsx

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,12 @@ function Field<Values = any>(props: FieldProps<Values>) {
6161
getFieldsValue,
6262
getFieldValue,
6363
getInternalHooks,
64+
setFieldValue,
65+
validateField,
6466
validateTrigger: fieldValidateTrigger
6567
} = fieldContext as unknown as InternalFormInstance<Values>;
6668

67-
const { dispatch, registerField, setFieldRules } = getInternalHooks();
69+
const { registerField, setFieldRules } = getInternalHooks();
6870

6971
const isControlled = controlMode === 'controlled';
7072

@@ -75,7 +77,7 @@ function Field<Values = any>(props: FieldProps<Values>) {
7577
const make =
7678
(evt: string) =>
7779
(..._args: any[]) =>
78-
dispatch({ name, opts: { trigger: evt }, type: 'validateField' });
80+
validateField(name, { trigger: evt });
7981

8082
const restValidateTriggerList = validateTriggerList
8183
.filter(item => item !== trigger)
@@ -115,19 +117,11 @@ function Field<Values = any>(props: FieldProps<Values>) {
115117
}
116118

117119
if (newValue !== oldValue) {
118-
dispatch({
119-
name,
120-
type: 'setFieldValue',
121-
value: newValue
122-
});
120+
setFieldValue(name, newValue);
123121
}
124122

125-
if (validateTriggerList.includes(trigger)) {
126-
dispatch({
127-
name,
128-
opts: { trigger },
129-
type: 'validateField'
130-
});
123+
if (validateTriggerList.includes(trigger) && rules) {
124+
validateField(name, { trigger });
131125
}
132126
}
133127
: undefined

primitives/filed-form /src/react/hooks/FieldContext.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ export interface OperationOptions<Values = any> {
8585
resetFields: (names?: AllPathsKeys<Values>[]) => void;
8686
submit: () => void;
8787
use: (mw: Middleware) => void;
88-
validateField: (name: AllPathsKeys<Values>) => Promise<boolean>;
88+
validateField: (name: AllPathsKeys<Values>, opts?: ValidateOptions) => Promise<boolean>;
8989
validateFields: (names?: AllPathsKeys<Values>[], opts?: ValidateFieldsOptions) => Promise<boolean>;
9090
}
9191

0 commit comments

Comments
 (0)