forked from Tencent/tdesign-react
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: form 新增 useWatch hook (Tencent#1490)
- Loading branch information
1 parent
c0e35ad
commit 3b4ec9e
Showing
11 changed files
with
218 additions
and
34 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import type { NamePath, FormInstanceFunctions } from '../type'; | ||
|
||
export type Store = Record<string, any>; | ||
|
||
export type WatchCallBack = (values: Store, namePathList: NamePath) => void; | ||
|
||
export interface InternalHooks { | ||
notifyWatch: (name: NamePath) => void; | ||
registerWatch: (callback: WatchCallBack) => () => void; | ||
} | ||
|
||
export interface InternalFormInstance extends FormInstanceFunctions { | ||
_init?: boolean; | ||
|
||
getInternalHooks?: (secret: Symbol) => InternalHooks | null; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,92 @@ | ||
import { useRef } from 'react'; | ||
import { FormInstanceFunctions } from '../type'; | ||
import { useState, useRef } from 'react'; | ||
import type { NamePath } from '../type'; | ||
import type { WatchCallBack, InternalHooks, InternalFormInstance, Store } from './interface'; | ||
import log from '../../_common/js/log'; | ||
|
||
export default function useForm() { | ||
const form = useRef<FormInstanceFunctions>({}); | ||
export const HOOK_MARK = Symbol('TD_FORM_INTERNAL_HOOKS'); | ||
|
||
return [form.current]; | ||
// TODO 后续将所有实例函数迁移到 FormStore 内统一管理 | ||
class FormStore { | ||
private store: Store = {}; | ||
|
||
private forceRootUpdate: () => void; | ||
|
||
constructor(forceRootUpdate: () => void) { | ||
this.forceRootUpdate = forceRootUpdate; | ||
} | ||
|
||
public getForm = (): InternalFormInstance => ({ | ||
submit: null, | ||
reset: null, | ||
validate: null, | ||
validateOnly: null, | ||
clearValidate: null, | ||
setFields: null, | ||
setFieldsValue: null, | ||
setValidateMessage: null, | ||
getFieldValue: null, | ||
getFieldsValue: null, | ||
_init: true, | ||
|
||
getInternalHooks: this.getInternalHooks, | ||
}); | ||
|
||
private getInternalHooks = (key: Symbol): InternalHooks | null => { | ||
if (key === HOOK_MARK) { | ||
return { | ||
notifyWatch: this.notifyWatch, | ||
registerWatch: this.registerWatch, | ||
}; | ||
} | ||
|
||
log.warn('Form', '`getInternalHooks` is internal usage. Should not call directly.'); | ||
return null; | ||
}; | ||
|
||
private watchList: WatchCallBack[] = []; | ||
|
||
private registerWatch: InternalHooks['registerWatch'] = (callback) => { | ||
this.watchList.push(callback); | ||
|
||
return () => { | ||
this.watchList = this.watchList.filter((fn) => fn !== callback); | ||
}; | ||
}; | ||
|
||
private notifyWatch = (namePath: NamePath = []) => { | ||
// No need to cost perf when nothing need to watch | ||
if (this.watchList.length) { | ||
const values = this.getFieldsValue?.(namePath); | ||
|
||
this.watchList.forEach((callback) => { | ||
callback(values, namePath); | ||
}); | ||
} | ||
}; | ||
|
||
// TODO 暂时先从组件初始化时外部 merge 覆盖相关 form 操作函数 | ||
private getFieldsValue = null; | ||
} | ||
|
||
export default function useForm(form?: InternalFormInstance) { | ||
const formRef = useRef<InternalFormInstance>({}); | ||
const [, forceUpdate] = useState({}); | ||
|
||
// eslint-disable-next-line | ||
if (!formRef.current._init) { | ||
if (form) { | ||
formRef.current = form; | ||
} else { | ||
// Create a new FormStore if not provided | ||
const forceReRender = () => { | ||
forceUpdate({}); | ||
}; | ||
|
||
const formStore: FormStore = new FormStore(forceReRender); | ||
|
||
formRef.current = formStore.getForm(); | ||
} | ||
} | ||
|
||
return [formRef.current]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import { useState, useEffect, useMemo, useRef } from 'react'; | ||
import type { NamePath } from '../type'; | ||
import type { InternalFormInstance } from './interface'; | ||
import { HOOK_MARK } from './useForm'; | ||
|
||
export default function useWatch(fields: NamePath, form: InternalFormInstance) { | ||
const [value, setValue] = useState<any>(); | ||
const valueStr = useMemo(() => JSON.stringify(value), [value]); | ||
const valueStrRef = useRef(valueStr); | ||
|
||
// eslint-disable-next-line | ||
const isValidForm = form && form._init; | ||
|
||
useEffect(() => { | ||
if (!isValidForm) return; | ||
|
||
const { getFieldValue, getInternalHooks } = form; | ||
const { registerWatch } = getInternalHooks(HOOK_MARK); | ||
|
||
// eslint-disable-next-line | ||
const cancelRegister = registerWatch((store) => { | ||
const newValue = getFieldValue(fields); | ||
const nextValueStr = JSON.stringify(newValue); | ||
|
||
// Compare stringify in case it's nest object | ||
if (valueStrRef.current !== nextValueStr) { | ||
valueStrRef.current = nextValueStr; | ||
setValue(newValue); | ||
} | ||
}); | ||
|
||
const initialValue = getFieldValue(fields); | ||
setValue(initialValue); | ||
|
||
return cancelRegister; | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
}, []); | ||
|
||
return value; | ||
} |
Oops, something went wrong.