@rc-component/form
Version:
React Form Component
73 lines (70 loc) • 2.69 kB
JavaScript
import warning from "rc-util/es/warning";
import FieldContext, { HOOK_MARK } from "./FieldContext";
import { useState, useContext, useEffect, useRef, useMemo } from 'react';
import { getNamePath, getValue } from "./utils/valueUtil";
import { isFormInstance } from "./utils/typeUtil";
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.');
} : () => {};
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 cancelRegister = registerWatch((values, allValues) => {
const newValue = getValue(options.preserve ? allValues : values, namePathRef.current);
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 = getValue(options.preserve ? getFieldsValue(true) : getFieldsValue(), namePathRef.current);
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;