Skip to content

Commit 364cf80

Browse files
committed
refactor(ui): optimize use of form
1 parent f3e554c commit 364cf80

21 files changed

+515
-496
lines changed

packages/platform/src/app/routes/Login.tsx

Lines changed: 51 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export default function Login(): JSX.Element | null {
3434
}
3535
}, [async, codeDisabled]);
3636

37-
const accountForm = useForm(
37+
const [accountForm, updateAccountForm] = useForm(
3838
() =>
3939
new FormGroup({
4040
username: new FormControl('', [
@@ -46,7 +46,7 @@ export default function Login(): JSX.Element | null {
4646
password: new FormControl('', Validators.required),
4747
})
4848
);
49-
const phoneForm = useForm(
49+
const [phoneForm, updatePhoneForm] = useForm(
5050
() =>
5151
new FormGroup({
5252
phone: new FormControl('', [Validators.required]),
@@ -67,7 +67,7 @@ export default function Login(): JSX.Element | null {
6767
<DForm.Item>
6868
<DButton
6969
type="submit"
70-
disabled={loginType === 'account' ? accountForm.form.invalid : phoneForm.form.invalid}
70+
disabled={loginType === 'account' ? !accountForm.valid : !phoneForm.valid}
7171
onClick={() => {
7272
const [http] = createHttp();
7373
setLoginLoading(true);
@@ -110,57 +110,61 @@ export default function Login(): JSX.Element | null {
110110
id: 'account',
111111
title: t('routes.login.Account Login'),
112112
panel: (
113-
<DForm dForm={accountForm} dLabelWidth={0}>
114-
<DForm.Item
115-
dFormControls={{
116-
username: {
117-
required: t('routes.login.Please enter your name'),
118-
checkValue: t('routes.login.Username'),
119-
},
120-
}}
121-
>
122-
{({ username }) => (
123-
<DInput dFormControl={username} dPrefix={<UserOutlined />} dPlaceholder={t('routes.login.Username')} />
124-
)}
125-
</DForm.Item>
126-
<DForm.Item dFormControls={{ password: t('routes.login.Please enter your password') }}>
127-
{({ password }) => (
128-
<DInput
129-
dFormControl={password}
130-
dPrefix={<LockOutlined />}
131-
dPlaceholder={t('routes.login.Password')}
132-
dType="password"
133-
/>
134-
)}
135-
</DForm.Item>
136-
{loginSameNode}
113+
<DForm dUpdate={updateAccountForm} dLabelWidth={0}>
114+
<DForm.Group dFormGroup={accountForm}>
115+
<DForm.Item
116+
dFormControls={{
117+
username: {
118+
required: t('routes.login.Please enter your name'),
119+
checkValue: t('routes.login.Username'),
120+
},
121+
}}
122+
>
123+
{({ username }) => (
124+
<DInput dFormControl={username} dPrefix={<UserOutlined />} dPlaceholder={t('routes.login.Username')} />
125+
)}
126+
</DForm.Item>
127+
<DForm.Item dFormControls={{ password: t('routes.login.Please enter your password') }}>
128+
{({ password }) => (
129+
<DInput
130+
dFormControl={password}
131+
dPrefix={<LockOutlined />}
132+
dPlaceholder={t('routes.login.Password')}
133+
dType="password"
134+
/>
135+
)}
136+
</DForm.Item>
137+
{loginSameNode}
138+
</DForm.Group>
137139
</DForm>
138140
),
139141
},
140142
{
141143
id: 'phone',
142144
title: t('routes.login.Phone Login'),
143145
panel: (
144-
<DForm dForm={phoneForm} dLabelWidth={0}>
145-
<DForm.Item dFormControls={{ phone: t('routes.login.Please enter your phone number') }}>
146-
{({ phone }) => (
147-
<DInput dFormControl={phone} dPrefix={<MobileOutlined />} dPlaceholder={t('routes.login.Phone number')} />
148-
)}
149-
</DForm.Item>
150-
<DForm.Item dFormControls={{ code: t('routes.login.Please enter verification code') }} dSpan>
151-
{({ code }) => <DInput dFormControl={code} dPlaceholder={t('routes.login.Verification code')} />}
152-
</DForm.Item>
153-
<DForm.Item dLabelWidth={8} dSpan="auto">
154-
<DButton
155-
disabled={codeDisabled > 0 || phoneForm.form.controls['phone'].invalid}
156-
onClick={() => {
157-
setCodeDisabled(60);
158-
}}
159-
>
160-
{codeDisabled > 0 ? `${codeDisabled}s` : t('routes.login.Get code')}
161-
</DButton>
162-
</DForm.Item>
163-
{loginSameNode}
146+
<DForm dUpdate={updatePhoneForm} dLabelWidth={0}>
147+
<DForm.Group dFormGroup={phoneForm}>
148+
<DForm.Item dFormControls={{ phone: t('routes.login.Please enter your phone number') }}>
149+
{({ phone }) => (
150+
<DInput dFormControl={phone} dPrefix={<MobileOutlined />} dPlaceholder={t('routes.login.Phone number')} />
151+
)}
152+
</DForm.Item>
153+
<DForm.Item dFormControls={{ code: t('routes.login.Please enter verification code') }} dSpan>
154+
{({ code }) => <DInput dFormControl={code} dPlaceholder={t('routes.login.Verification code')} />}
155+
</DForm.Item>
156+
<DForm.Item dLabelWidth={8} dSpan="auto">
157+
<DButton
158+
disabled={codeDisabled > 0 || !phoneForm.controls['phone'].valid}
159+
onClick={() => {
160+
setCodeDisabled(60);
161+
}}
162+
>
163+
{codeDisabled > 0 ? `${codeDisabled}s` : t('routes.login.Get code')}
164+
</DButton>
165+
</DForm.Item>
166+
{loginSameNode}
167+
</DForm.Group>
164168
</DForm>
165169
),
166170
},

packages/ui/src/components/_base-design/BaseDesign.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { DFormControl } from '../form';
33
import React, { useContext } from 'react';
44

55
import { DComposeContext } from '../compose';
6-
import { DFormContext } from '../form';
6+
import { DFormUpdateContext } from '../form';
77

88
export interface DBaseDesignProps {
99
children: React.ReactElement;
@@ -18,10 +18,10 @@ export function DBaseDesign(props: DBaseDesignProps): JSX.Element | null {
1818
const { children, dCompose, dFormControl } = props;
1919

2020
const composeContext = useContext(DComposeContext);
21-
const formContext = useContext(DFormContext);
21+
const updateForm = useContext(DFormUpdateContext);
2222

2323
const supportCompose = composeContext && dCompose;
24-
const supportForm = formContext && dFormControl;
24+
const supportForm = updateForm && dFormControl;
2525

2626
let dataAttrs: { [index: string]: boolean } = {};
2727

packages/ui/src/components/_base-input/BaseInput.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { isString } from 'lodash';
44
import React, { useContext, useId } from 'react';
55

66
import { usePrefixConfig } from '../../hooks';
7-
import { DFormContext } from '../form';
7+
import { DFormUpdateContext } from '../form';
88

99
export interface DBaseInputProps extends React.InputHTMLAttributes<any> {
1010
dFormControl?: DFormControl;
@@ -23,13 +23,13 @@ function BaseInput(props: DBaseInputProps, ref: React.ForwardedRef<any>): JSX.El
2323

2424
//#region Context
2525
const dPrefix = usePrefixConfig();
26-
const formContext = useContext(DFormContext);
26+
const updateForm = useContext(DFormUpdateContext);
2727
//#endregion
2828

2929
const uniqueId = useId();
3030
const id = restProps.id ?? `${dPrefix}base-input-${uniqueId}`;
3131

32-
const supportForm = formContext && dFormControl;
32+
const supportForm = updateForm && dFormControl;
3333

3434
const attrs = supportForm
3535
? {

packages/ui/src/components/form/Form.tsx

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,31 @@
11
import type { DSize } from '../../utils/types';
22
import type { DBreakpoints } from '../grid';
3-
import type { DFormInstance } from './hooks';
43

54
import { isUndefined } from 'lodash';
6-
import React, { useMemo } from 'react';
5+
import React from 'react';
76

87
import { getClassName } from '@react-devui/utils';
98

109
import { usePrefixConfig, useComponentConfig, useMediaQuery } from '../../hooks';
1110
import { registerComponentMate } from '../../utils';
12-
import { DFormGroup, DFormGroupContext } from './FormGroup';
11+
import { DFormGroup } from './FormGroup';
1312
import { DFormItem } from './FormItem';
13+
import { DFormUpdateContext } from './hooks';
1414

1515
export interface DFormContextData {
16-
gInstance: DFormInstance;
17-
gBreakpointsMatched: DBreakpoints[];
1816
gLabelWidth: NonNullable<DFormProps['dLabelWidth']>;
1917
gLabelColon: NonNullable<DFormProps['dLabelColon']>;
2018
gRequiredType: NonNullable<DFormProps['dRequiredType']>;
2119
gLayout: NonNullable<DFormProps['dLayout']>;
2220
gInlineSpan: NonNullable<DFormProps['dInlineSpan']>;
2321
gFeedbackIcon: NonNullable<DFormProps['dFeedbackIcon']>;
2422
gSize?: DSize;
23+
gBreakpointsMatched: DBreakpoints[];
2524
}
2625
export const DFormContext = React.createContext<DFormContextData | null>(null);
2726

2827
export interface DFormProps extends React.FormHTMLAttributes<HTMLFormElement> {
29-
dForm: DFormInstance;
28+
dUpdate: () => void;
3029
dLabelWidth?: number | string;
3130
dLabelColon?: boolean;
3231
dRequiredType?: 'required' | 'optional' | 'hidden';
@@ -52,7 +51,7 @@ export const DForm: {
5251
} = (props) => {
5352
const {
5453
children,
55-
dForm,
54+
dUpdate,
5655
dLabelWidth,
5756
dLabelColon,
5857
dRequiredType = 'required',
@@ -71,17 +70,16 @@ export const DForm: {
7170

7271
const breakpointsMatched = useMediaQuery();
7372

74-
const contextValue = useMemo<DFormContextData>(() => {
75-
const contextValue = {
76-
gInstance: dForm,
77-
gBreakpointsMatched: breakpointsMatched,
73+
const contextValue = (() => {
74+
const contextValue: DFormContextData = {
7875
gLabelWidth: dLabelWidth ?? 150,
7976
gLabelColon: dLabelColon ?? true,
8077
gRequiredType: dRequiredType,
8178
gLayout: dLayout,
8279
gInlineSpan: dInlineSpan,
8380
gFeedbackIcon: dFeedbackIcon,
8481
gSize: dSize,
82+
gBreakpointsMatched: breakpointsMatched,
8583
};
8684
if (dResponsiveProps) {
8785
const mergeProps = (point: string, targetKey: string, sourceKey: string) => {
@@ -103,11 +101,11 @@ export const DForm: {
103101
contextValue.gLabelColon = dLabelColon ?? (contextValue.gLayout === 'vertical' ? false : true);
104102

105103
return contextValue;
106-
}, [dForm, breakpointsMatched, dLabelWidth, dLabelColon, dRequiredType, dLayout, dInlineSpan, dFeedbackIcon, dSize, dResponsiveProps]);
104+
})();
107105

108106
return (
109107
<DFormContext.Provider value={contextValue}>
110-
<DFormGroupContext.Provider value={dForm.form}>
108+
<DFormUpdateContext.Provider value={dUpdate}>
111109
<form
112110
{...restProps}
113111
className={getClassName(restProps.className, `${dPrefix}form`, {
@@ -124,7 +122,7 @@ export const DForm: {
124122
>
125123
{children}
126124
</form>
127-
</DFormGroupContext.Provider>
125+
</DFormUpdateContext.Provider>
128126
</DFormContext.Provider>
129127
);
130128
};

packages/ui/src/components/form/FormItem.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export function DFormItem<T extends { [index: string]: DErrorInfo }>(props: DFor
6262
//#region Context
6363
const dPrefix = usePrefixConfig();
6464
const { dColNum } = useGridConfig();
65-
const { gBreakpointsMatched, gLabelWidth, gLabelColon, gRequiredType, gLayout, gInlineSpan, gFeedbackIcon } =
65+
const { gLabelWidth, gLabelColon, gRequiredType, gLayout, gInlineSpan, gFeedbackIcon, gBreakpointsMatched } =
6666
useContextRequired(DFormContext);
6767
const formGroup = useContext(DFormGroupContext)!;
6868
//#endregion
@@ -72,6 +72,10 @@ export function DFormItem<T extends { [index: string]: DErrorInfo }>(props: DFor
7272
const contentRef = useRef<HTMLDivElement>(null);
7373
//#endregion
7474

75+
if (!formGroup) {
76+
throw new Error('jnnnnnnnn');
77+
}
78+
7579
const [t] = useTranslation();
7680

7781
const uniqueId = useId();

packages/ui/src/components/form/abstract-control.ts

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,10 @@ export abstract class AbstractControl {
185185

186186
public readonly asyncVerifyComplete$ = new Subject<AbstractControl>();
187187

188+
clone(): typeof this {
189+
return new Proxy(this, {});
190+
}
191+
188192
setValidators(validators: ValidatorFn | ValidatorFn[] | null): void {
189193
this._rawValidators = validators;
190194
this._composedValidatorFn = coerceToValidator(validators);
@@ -330,16 +334,6 @@ export abstract class AbstractControl {
330334

331335
protected abstract _allControlsDisabled(): boolean;
332336

333-
protected _isBoxedValue(formState: any): boolean {
334-
return (
335-
typeof formState === 'object' &&
336-
formState !== null &&
337-
Object.keys(formState).length === 2 &&
338-
'value' in formState &&
339-
'disabled' in formState
340-
);
341-
}
342-
343337
protected _anyControlsHaveStatus(status: FormControlStatus): boolean {
344338
return this._anyControls((control) => control.status === status);
345339
}

packages/ui/src/components/form/demos/1.Basic.md

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ The simplest usage.
1616
import { DForm, FormControl, FormGroup, Validators, useForm, DInput, DButton } from '@react-devui/ui';
1717

1818
export default function Demo() {
19-
const formInstance = useForm(
19+
const [form, updateForm] = useForm(
2020
() =>
2121
new FormGroup({
2222
username: new FormControl('', Validators.required),
@@ -25,18 +25,20 @@ export default function Demo() {
2525
);
2626

2727
return (
28-
<DForm dForm={formInstance} dLabelWidth={120}>
29-
<DForm.Item dFormControls={{ username: 'Please input your username!' }} dLabel="Username">
30-
{({ username }) => <DInput dFormControl={username} dPlaceholder="Username" />}
31-
</DForm.Item>
32-
<DForm.Item dFormControls={{ password: 'Please input your password!' }} dLabel="Password">
33-
{({ password }) => <DInput dFormControl={password} dPlaceholder="Password" dType="password" />}
34-
</DForm.Item>
35-
<DForm.Item>
36-
<DButton type="submit" disabled={!formInstance.form.valid}>
37-
Submit
38-
</DButton>
39-
</DForm.Item>
28+
<DForm dUpdate={updateForm} dLabelWidth={120}>
29+
<DForm.Group dFormGroup={form}>
30+
<DForm.Item dFormControls={{ username: 'Please input your username!' }} dLabel="Username">
31+
{({ username }) => <DInput dFormControl={username} dPlaceholder="Username" />}
32+
</DForm.Item>
33+
<DForm.Item dFormControls={{ password: 'Please input your password!' }} dLabel="Password">
34+
{({ password }) => <DInput dFormControl={password} dPlaceholder="Password" dType="password" />}
35+
</DForm.Item>
36+
<DForm.Item>
37+
<DButton type="submit" disabled={!form.valid}>
38+
Submit
39+
</DButton>
40+
</DForm.Item>
41+
</DForm.Group>
4042
</DForm>
4143
);
4244
}

0 commit comments

Comments
 (0)