react-tiniest-form
Version:
the tiniest form
120 lines (119 loc) • 5.13 kB
JavaScript
import { useRef, useState } from 'react';
import { createFormsStore, parseToInputValue } from '../../utils/formStore/createFormStore';
import { invariantOf } from '../../utils/@common/invariantOf';
const useFormStore = (options) => {
const formStore = useRef(createFormsStore(options)).current;
const [snapshot, setSnapshot] = useState(formStore.store);
const [errors, setErrors] = useState({});
const setError = (name, info) => {
setErrors(errors => (Object.assign(Object.assign({}, errors), { [name]: Object.assign(Object.assign({}, errors[name]), info) })));
};
const deleteError = (name) => {
if (errors[name]) {
setErrors(errors => {
const clonedErrors = structuredClone(errors);
delete clonedErrors[name];
return clonedErrors;
});
}
};
const takeSnapShot = (name, value) => {
setSnapshot(prev => (Object.assign(Object.assign({}, prev), { [name]: Object.assign(Object.assign({}, prev[name]), { value }) })));
};
return {
store: formStore,
snapshot,
errors,
setError,
takeSnapShot,
deleteError,
};
};
const useForm = (options) => {
const { store: { store, errors, registerField, getFieldValue, updateFieldValue, watchField, isWatching, validateField, getFieldInfo, }, errors: errorsSnapshot, setError, deleteError, takeSnapShot, } = useFormStore(options);
const watch = (fieldsNames) => {
return fieldsNames.reduce((acc, name) => {
watchField(name);
return Object.assign(Object.assign({}, acc), {
/**@todo snapshot or getFieldValue */
[name]: getFieldValue(name) });
}, {});
};
const handleValidation = (name) => {
return validateField({
name,
onValid: () => deleteError(name),
onInvalid: ({ type, message }) => { var _a; return (!errors[name] || ((_a = errorsSnapshot[name]) === null || _a === void 0 ? void 0 : _a.type) !== type) && setError(name, { type, message }); },
});
};
const register = (name, options) => {
const isControlledValue = (options === null || options === void 0 ? void 0 : options.value) !== undefined;
const controlledValue = parseToInputValue(options === null || options === void 0 ? void 0 : options.value);
const onChange = (e) => {
var _a;
const { target: { value }, } = e;
updateFieldValue(name, { value });
handleValidation(name);
if (isWatching(name))
takeSnapShot(name, value);
(_a = options === null || options === void 0 ? void 0 : options.onChange) === null || _a === void 0 ? void 0 : _a.call(options, e);
};
const onBlur = (e) => {
var _a;
handleValidation(name);
(_a = options === null || options === void 0 ? void 0 : options.onBlur) === null || _a === void 0 ? void 0 : _a.call(options, e);
};
const ref = instance => {
var _a;
if (!instance)
return;
if (!((_a = getFieldInfo(name)) === null || _a === void 0 ? void 0 : _a.registered)) {
const defaultValue = getFieldValue(name);
registerField(name, {
value: controlledValue || defaultValue,
validations: options === null || options === void 0 ? void 0 : options.validations,
ref: instance,
});
instance.value = getFieldValue(name);
}
};
return Object.assign({ name,
onChange,
onBlur,
ref }, (isControlledValue ? { value: controlledValue } : {}));
};
const handleSubmit = (onValid, onInvalid) => {
const onSubmit = (e) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
const formFields = Object.fromEntries(formData);
/**@todo schema check */
const isAllValid = Object.keys(invariantOf(formFields)).every(name => {
var _a;
const info = getFieldInfo(name);
/**@todo use store errors */
const isValid = handleValidation(name);
if (!isValid)
(_a = info === null || info === void 0 ? void 0 : info.ref) === null || _a === void 0 ? void 0 : _a.focus();
return isValid;
});
isAllValid ? onValid === null || onValid === void 0 ? void 0 : onValid(formFields, e) : onInvalid === null || onInvalid === void 0 ? void 0 : onInvalid(errors, e);
};
return onSubmit;
};
const getFieldState = (name) => {
const info = getFieldInfo(name);
return info ? { isValid: info.isValid, ref: info.ref } : undefined;
};
return {
store,
errors,
setError,
register,
watch,
handleSubmit,
getFieldValue,
getFieldState,
};
};
export { useForm };