laravel-precognition-react
Version:
Laravel Precognition (React).
185 lines (184 loc) • 5.73 kB
JavaScript
import { resolveName, client, createValidator, toSimpleValidationErrors, resolveUrl, resolveMethod } from 'laravel-precognition';
import { cloneDeep, get, set } from 'lodash-es';
import { useRef, useState } from 'react';
export { client };
export const useForm = (method, url, input, config = {}) => {
/**
* The original data.
*/
const originalData = useRef(null);
if (originalData.current === null) {
originalData.current = cloneDeep(input);
}
/**
* The original input names.
*/
const originalInputs = useRef(null);
if (originalInputs.current === null) {
originalInputs.current = Object.keys(originalData);
}
/**
* The current payload.
*/
const payload = useRef(originalData.current);
/**
* The reactive valid state.
*/
const [valid, setValid] = useState([]);
/**
* The reactive touched state.
*/
const [touched, setTouched] = useState([]);
/**
* The reactive validating state.
*/
const [validating, setValidating] = useState(false);
/**
* The reactive validating state.
*/
const [processing, setProcessing] = useState(false);
/**
* The reactive errors state.
*/
const [errors, setErrors] = useState({});
/**
* The reactive hasErrors state.
*/
const [hasErrors, setHasErrors] = useState(false);
/**
* The reactive data state.
*/
const [data, setData] = useState(() => cloneDeep(originalData.current));
/**
* The validator instance.
*/
const validator = useRef(null);
if (validator.current === null) {
validator.current = createValidator((client) => client[resolveMethod(method)](resolveUrl(url), payload.current, config), input)
.on('validatingChanged', () => {
setValidating(validator.current.validating());
})
.on('validatedChanged', () => {
setValid(validator.current.valid());
})
.on('touchedChanged', () => {
setTouched(validator.current.touched());
})
.on('errorsChanged', () => {
setHasErrors(validator.current.hasErrors());
// @ts-expect-error
setErrors(toSimpleValidationErrors(validator.current.errors()));
setValid(validator.current.valid());
});
}
/**
* Resolve the config for a form submission.
*/
const resolveSubmitConfig = (config) => ({
...config,
precognitive: false,
onStart: () => {
setProcessing(true);
(config.onStart ?? (() => null))();
},
onFinish: () => {
setProcessing(false);
(config.onFinish ?? (() => null))();
},
onValidationError: (response, error) => {
validator.current.setErrors(response.data.errors);
return config.onValidationError
? config.onValidationError(response)
: Promise.reject(error);
},
});
/**
* The form instance.
*/
const form = {
data,
setData(key, value) {
if (typeof key === 'object') {
payload.current = key;
setData(key);
}
else {
const newData = cloneDeep(payload.current);
payload.current = set(newData, key, value);
setData(payload.current);
}
return form;
},
touched(name) {
return touched.includes(name);
},
touch(name) {
validator.current.touch(name);
return form;
},
validate(name, config) {
if (typeof name === 'object' && !('target' in name)) {
config = name;
name = undefined;
}
if (typeof name === 'undefined') {
validator.current.validate(config);
}
else {
// @ts-expect-error
name = resolveName(name);
validator.current.validate(name, get(payload.current, name), config);
}
return form;
},
validating,
valid(name) {
return valid.includes(name);
},
invalid(name) {
return typeof errors[name] !== 'undefined';
},
errors,
hasErrors,
setErrors(errors) {
// @ts-expect-error
validator.current.setErrors(errors);
return form;
},
forgetError(name) {
// @ts-expect-error
validator.current.forgetError(name);
return form;
},
reset(...names) {
const original = cloneDeep(originalData.current);
if (names.length === 0) {
payload.current = original;
setData(original);
}
else {
names.forEach((name) => (set(payload.current, name, get(original, name))));
setData(payload.current);
}
// @ts-expect-error
validator.current.reset(...names);
return form;
},
setValidationTimeout(duration) {
validator.current.setTimeout(duration);
return form;
},
processing,
async submit(config = {}) {
return client[resolveMethod(method)](resolveUrl(url), payload.current, resolveSubmitConfig(config));
},
validateFiles() {
validator.current.validateFiles();
return form;
},
validator() {
return validator.current;
},
};
return form;
};