UNPKG

@rc-component/form

Version:
87 lines (80 loc) 3.13 kB
import warning from "@rc-component/util/es/warning"; import { useContext, useEffect, useMemo, useRef, useState } from 'react'; import FieldContext, { HOOK_MARK } from "./FieldContext"; import { isFormInstance } from "./utils/typeUtil"; import { getNamePath, getValue } from "./utils/valueUtil"; export function stringify(value) { try { return JSON.stringify(value); } catch (err) { return Math.random(); } } const useWatchWarning = process.env.NODE_ENV !== 'production' ? namePath => { const fullyStr = namePath.join('__RC_FIELD_FORM_SPLIT__'); const nameStrRef = useRef(fullyStr); warning(nameStrRef.current === fullyStr, '`useWatch` is not support dynamic `namePath`. Please provide static instead.'); } : () => {}; // ------- selector type ------- // ------- selector type end ------- function useWatch(...args) { const [dependencies, _form = {}] = args; const options = isFormInstance(_form) ? { form: _form } : _form; const form = options.form; const [value, setValue] = useState(); const valueStr = useMemo(() => stringify(value), [value]); const valueStrRef = useRef(valueStr); valueStrRef.current = valueStr; const fieldContext = useContext(FieldContext); const formInstance = form || fieldContext; const isValidForm = formInstance && formInstance._init; // Warning if not exist form instance if (process.env.NODE_ENV !== 'production') { warning(args.length === 2 ? form ? isValidForm : true : isValidForm, 'useWatch requires a form instance since it can not auto detect from context.'); } const namePath = getNamePath(dependencies); const namePathRef = useRef(namePath); namePathRef.current = namePath; useWatchWarning(namePath); useEffect(() => { // Skip if not exist form instance if (!isValidForm) { return; } const { getFieldsValue, getInternalHooks } = formInstance; const { registerWatch } = getInternalHooks(HOOK_MARK); const getWatchValue = (values, allValues) => { const watchValue = options.preserve ? allValues : values; return typeof dependencies === 'function' ? dependencies(watchValue) : getValue(watchValue, namePathRef.current); }; const cancelRegister = registerWatch((values, allValues) => { const newValue = getWatchValue(values, allValues); const nextValueStr = stringify(newValue); // Compare stringify in case it's nest object if (valueStrRef.current !== nextValueStr) { valueStrRef.current = nextValueStr; setValue(newValue); } }); // TODO: We can improve this perf in future const initialValue = getWatchValue(getFieldsValue(), getFieldsValue(true)); // React 18 has the bug that will queue update twice even the value is not changed // ref: https://github.com/facebook/react/issues/27213 if (value !== initialValue) { setValue(initialValue); } return cancelRegister; }, // We do not need re-register since namePath content is the same // eslint-disable-next-line react-hooks/exhaustive-deps [isValidForm]); return value; } export default useWatch;