Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 18 additions & 7 deletions src/components/Form/FormItem/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,14 @@ import { useId } from '../../../utils/hooks'
*/
const Item: React.FC<HuiFormItemProps> = (props) => {
const context = useContext<FieldContext>(Context)
const { registerWatch, getFieldValue, setFieldValue, removeFieldValue } =
context
const {
registerWatch,
getFieldValue,
getFieldInfo,
setFieldValue,
removeFieldValue,
updateShouldValidate,
} = context
const listContext = useContext<FormListContextProps>(FormListContext)
const [renderType, setRenderType] = useState<keyof ItemType>('other')
const [, update] = useState({})
Expand Down Expand Up @@ -118,7 +124,7 @@ const Item: React.FC<HuiFormItemProps> = (props) => {
</View>
)

const localValue = getFieldValue(path)
const [localValue, shouldValidate] = getFieldInfo(path)

const onChange = useCallback(
(event) => {
Expand Down Expand Up @@ -174,15 +180,20 @@ const Item: React.FC<HuiFormItemProps> = (props) => {
name: path,
}
} catch (error) {
throw new Error(error)
throw new Error(error as string)
} finally {
updateShouldValidate(path, true)
}
},
[getFieldValue, path, renderType, rule],
[getFieldValue, path, renderType, rule, updateShouldValidate],
)

useEffect(() => {
validatorRules(localValue)
}, [localValue])
// 如果shouldValidate为false,则不进行验证
if (shouldValidate) {
validatorRules(localValue)
}
}, [localValue, shouldValidate])

// 组件卸载后移出字段
useEffect(() => () => removeFieldValue(path), [path, removeFieldValue])
Expand Down
43 changes: 41 additions & 2 deletions src/components/Form/FormStore/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import unset from 'lodash/unset'
import { toArray, getErrorTarget, toString } from '../util'
import {
FieldContext,
FieldOptions,
FieldWatchCallback,
registerWatchType,
} from '../constants'
Expand All @@ -31,6 +32,8 @@ class FormStore {

timer: number | undefined

shouldValidateMap: Record<string, boolean>

constructor() {
this.store = {}

Expand All @@ -53,6 +56,8 @@ class FormStore {
this.pendingUnset = []

this.timer = undefined

this.shouldValidateMap = {}
}

registerWatch(id: string, field: registerWatchType): () => void {
Expand Down Expand Up @@ -112,19 +117,37 @@ class FormStore {

getFieldsValue = (): any => this.store

setFieldValue<T>(name: string | string[], value: T): void {
setFieldValue<T>(
name: string | string[],
value: T,
options?: FieldOptions,
): void {
if (this.getFieldValue(name) !== value) {
set(this.store, toArray(name), value)
set(
this.shouldValidateMap,
toArray(name),
options?.shouldValidate ?? true,
)
this.handleChange({ name, value })
this.notifywatchList(name)
}
}

setFieldsValue(newStore: Store): void {
setFieldsValue(newStore: Store, options?: FieldOptions): void {
const newStoreTarget = {
...this.store,
...newStore,
}
for (const key in newStore) {
if (Object.prototype.hasOwnProperty.call(newStore, key)) {
set(
this.shouldValidateMap,
toArray(key),
options?.shouldValidate ?? true,
)
}
}
this.updateStore(newStoreTarget)
this.handleChange()
this.notifywatchList()
Expand Down Expand Up @@ -236,6 +259,20 @@ class FormStore {
} catch (error) {}
}

private updateShouldValidate(
name: string | string[],
shouldValidate: boolean,
): void {
this.shouldValidateMap[toArray(name)?.join()] = shouldValidate
}

private getFieldInfo(name: string | string[]): [any, boolean] {
return [
this.getFieldValue(name),
this.shouldValidateMap[toArray(name)?.join()] ?? true,
]
}

getForm(): FieldContext {
return {
removeFieldValue: this.removeFieldValue.bind(this),
Expand All @@ -249,6 +286,8 @@ class FormStore {
registerFieldWatch: this.registerFieldWatch.bind(this),
submit: this.submit.bind(this),
reset: this.reset.bind(this),
updateShouldValidate: this.updateShouldValidate.bind(this),
getFieldInfo: this.getFieldInfo.bind(this),
}
}
}
Expand Down
22 changes: 20 additions & 2 deletions src/components/Form/constants/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ export type NamePath = string | string[]

export type FieldWatchCallback = (values: any) => void

export type FieldOptions = {
/** 是否需要校验 */
shouldValidate?: boolean
}

export interface FieldContext {
/** 删除单个表单数据 */
removeFieldValue: (name: string | string[]) => void
Expand All @@ -22,9 +27,13 @@ export interface FieldContext {
/** 获取所有表单数据 */
getFieldsValue: () => any
/** 设置所有表单数据 */
setFieldsValue: (sotre: any) => any
setFieldsValue: (sotre: any, options?: FieldOptions) => any
/** 设置单个表单数据 */
setFieldValue: (name: string | string[], value: any) => void
setFieldValue: (
name: string | string[],
value: any,
options?: FieldOptions,
) => void
/** 验证表单 */
validatorFields: () => any
/** 注册监听 */
Expand All @@ -37,6 +46,13 @@ export interface FieldContext {
submit: () => Promise<{ type: 'success' | 'fail'; data: any }>
/** 重制表单 */
reset: () => Promise<void>
/** 更新校验状态 */
updateShouldValidate: (
name: string | string[],
shouldValidate: boolean,
) => void
/** 获取字段信息 */
getFieldInfo: (name: string | string[]) => any
}

const warningFunc = () => {}
Expand All @@ -53,6 +69,8 @@ const Context = React.createContext<FieldContext>({
setCallbacks: warningFunc,
submit: () => Promise.resolve().then(warningFunc as any),
reset: () => Promise.resolve().then(warningFunc),
updateShouldValidate: warningFunc,
getFieldInfo: warningFunc,
})

export interface FormItemContextProps {
Expand Down
8 changes: 4 additions & 4 deletions src/pages/Form/Form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import GroupSection from '@/demoComponents/GroupSection'
import PageHeader from '@/demoComponents/PageHeader'
import HuiRadio from '@/components/Radio'

import ShouldValidateForm from './components/shouldValidateForm'

import './Form.scss'

const BooleanRadioGroup: React.FC<{
Expand Down Expand Up @@ -95,10 +97,6 @@ const InputPage: React.FC = () => {
setOpen1(false)
}

useEffect(() => {
form.setFieldsValue(localData)
}, [])

const handleChange1 = useCallback((_cur, formData) => {
setLocalData({ ...formData })
}, [])
Expand Down Expand Up @@ -320,6 +318,8 @@ const InputPage: React.FC = () => {
</HuiFormItem>
</HuiForm>
</GroupSection>

<ShouldValidateForm />
</View>
</View>
)
Expand Down
103 changes: 103 additions & 0 deletions src/pages/Form/components/shouldValidateForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import React, { useCallback, useEffect } from 'react'
import HuiForm, { useForm } from '@/components/Form/index'
import HuiButton from '@/components/Button/Button'
import GroupSection from '@/demoComponents/GroupSection'
import Taro from '@tarojs/taro'
import HuiInput from '@/components/Input'
import HuiTextArea from '@/components/TextArea'

const HuiFormItem = HuiForm.Item

const ShouldValidate: React.FC = () => {
const [form] = useForm()

const handleReset = useCallback((f) => {
Taro.showModal({
title: '重置',
content: `重置成功:'${JSON.stringify(f.getFieldsValue())}`,
})
}, [])

const handleFinish = useCallback((data) => {
Taro.showModal({
title: '提示',
content: `提交成功:'${JSON.stringify(data)}`,
})
}, [])

useEffect(() => {
form.setFieldsValue(
{
account: '1234567890',
password: '',
description: '',
},
{ shouldValidate: false },
)
}, [form])

return (
<GroupSection title='form 校验时机'>
<HuiForm
form={form}
onFinish={handleFinish}
onReset={() => handleReset(form)}
>
<HuiFormItem
rule={[
{
require: true,
},
(value) => value,
]}
label={(v) => `账号长度: ${v?.length ?? 0}`}
name='account'
>
<HuiInput
divider={false}
onInput={(e) => form.setFieldValue('account', e.detail.value)}
></HuiInput>
</HuiFormItem>
<HuiFormItem
label='密码'
name='password'
rule={[
{
require: true,
message: '密码必须填写',
},
]}
tipsText='密码必须为6-10个字符之间'
>
<HuiInput
divider={false}
onInput={(e) => form.setFieldValue('password', e.detail.value)}
type='safe-password'
></HuiInput>
</HuiFormItem>

<HuiFormItem
rule={[{ require: true, message: '简介必须填写' }]}
align='column'
label='简介'
name='description'
>
<HuiTextArea
required={false}
upperLimit={50}
onInput={(e) => form.setFieldValue('description', e.detail.value)}
></HuiTextArea>
</HuiFormItem>

<HuiButton formType='submit' block style={{ margin: '12px 0' }}>
提交表单
</HuiButton>
<HuiButton formType='reset' type='secondary' block>
重置表单
</HuiButton>
</HuiForm>
</GroupSection>
)
}

export default ShouldValidate
Loading