@rc-component/form
Version:
React Form Component
139 lines (129 loc) • 4.3 kB
JavaScript
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
import * as React from 'react';
import useForm from "./hooks/useForm";
import FieldContext, { HOOK_MARK } from "./FieldContext";
import FormContext from "./FormContext";
import { isSimilar } from "./utils/valueUtil";
import ListContext from "./ListContext";
const Form = (props, ref) => {
const {
name,
initialValues,
fields,
form,
preserve,
children,
component: Component = 'form',
validateMessages,
validateTrigger = 'onChange',
onValuesChange,
onFieldsChange,
onFinish,
onFinishFailed,
clearOnDestroy,
...restProps
} = props;
const nativeElementRef = React.useRef(null);
const formContext = React.useContext(FormContext);
// We customize handle event since Context will makes all the consumer re-render:
// https://reactjs.org/docs/context.html#contextprovider
const [formInstance] = useForm(form);
const {
useSubscribe,
setInitialValues,
setCallbacks,
setValidateMessages,
setPreserve,
destroyForm
} = formInstance.getInternalHooks(HOOK_MARK);
// Pass ref with form instance
React.useImperativeHandle(ref, () => ({
...formInstance,
nativeElement: nativeElementRef.current
}));
// Register form into Context
React.useEffect(() => {
formContext.registerForm(name, formInstance);
return () => {
formContext.unregisterForm(name);
};
}, [formContext, formInstance, name]);
// Pass props to store
setValidateMessages({
...formContext.validateMessages,
...validateMessages
});
setCallbacks({
onValuesChange,
onFieldsChange: (changedFields, ...rest) => {
formContext.triggerFormChange(name, changedFields);
if (onFieldsChange) {
onFieldsChange(changedFields, ...rest);
}
},
onFinish: values => {
formContext.triggerFormFinish(name, values);
if (onFinish) {
onFinish(values);
}
},
onFinishFailed
});
setPreserve(preserve);
// Set initial value, init store value when first mount
const mountRef = React.useRef(null);
setInitialValues(initialValues, !mountRef.current);
if (!mountRef.current) {
mountRef.current = true;
}
// ========================== Unmount ===========================
React.useEffect(() => () => destroyForm(clearOnDestroy),
// eslint-disable-next-line react-hooks/exhaustive-deps
[]);
// Prepare children by `children` type
let childrenNode;
const childrenRenderProps = typeof children === 'function';
if (childrenRenderProps) {
const values = formInstance.getFieldsValue(true);
childrenNode = children(values, formInstance);
} else {
childrenNode = children;
}
// Not use subscribe when using render props
useSubscribe(!childrenRenderProps);
// Listen if fields provided. We use ref to save prev data here to avoid additional render
const prevFieldsRef = React.useRef(null);
React.useEffect(() => {
if (!isSimilar(prevFieldsRef.current || [], fields || [])) {
formInstance.setFields(fields || []);
}
prevFieldsRef.current = fields;
}, [fields, formInstance]);
// =========================== Render ===========================
const formContextValue = React.useMemo(() => ({
...formInstance,
validateTrigger
}), [formInstance, validateTrigger]);
const wrapperNode = /*#__PURE__*/React.createElement(ListContext.Provider, {
value: null
}, /*#__PURE__*/React.createElement(FieldContext.Provider, {
value: formContextValue
}, childrenNode));
if (Component === false) {
return wrapperNode;
}
return /*#__PURE__*/React.createElement(Component, _extends({}, restProps, {
ref: nativeElementRef,
onSubmit: event => {
event.preventDefault();
event.stopPropagation();
formInstance.submit();
},
onReset: event => {
event.preventDefault();
formInstance.resetFields();
restProps.onReset?.(event);
}
}), wrapperNode);
};
export default Form;