mirror of
https://github.com/filecoin-project/slate.git
synced 2024-09-20 02:37:09 +03:00
221 lines
6.1 KiB
JavaScript
221 lines
6.1 KiB
JavaScript
import * as React from "react";
|
|
|
|
export const useMounted = () => {
|
|
const isMounted = React.useRef(true);
|
|
React.useLayoutEffect(() => {
|
|
return () => {
|
|
isMounted.current = false;
|
|
};
|
|
}, []);
|
|
return isMounted.current;
|
|
};
|
|
|
|
/** NOTE(amine):
|
|
* useForm handles three main responsabilities
|
|
* - control inputs
|
|
* - control form
|
|
* - add validations
|
|
*
|
|
* For validations
|
|
* - Validate each field when onBlur event is triggered
|
|
* - Validate all fields before submit
|
|
* - font submit if there is errors
|
|
*/
|
|
|
|
export const useForm = ({
|
|
onSubmit,
|
|
validate,
|
|
initialValues,
|
|
validateOnBlur = true,
|
|
validateOnSubmit = true,
|
|
}) => {
|
|
const [internal, setInternal] = React.useState({ isSubmitting: false, isValidating: false });
|
|
const [state, setState] = React.useState({
|
|
isSubmitting: false,
|
|
values: initialValues,
|
|
errors: {},
|
|
touched: {},
|
|
});
|
|
|
|
const _hasError = (obj) => Object.keys(obj).some((name) => obj[name]);
|
|
const _mergeEventHandlers = (events = []) => (e) =>
|
|
events.forEach((event) => {
|
|
if (event) event(e);
|
|
});
|
|
|
|
/** ---------- NOTE(amine): Input Handlers ---------- */
|
|
const handleFieldChange = (e) =>
|
|
setState((prev) => ({
|
|
...prev,
|
|
values: { ...prev.values, [e.target.name]: e.target.value },
|
|
errors: { ...prev.errors, [e.target.name]: undefined },
|
|
touched: { ...prev.touched, [e.target.name]: false },
|
|
}));
|
|
|
|
const handleOnBlur = async (e) => {
|
|
// NOTE(amine): validate the inputs onBlur and touch the current input
|
|
let errors = {};
|
|
if (validateOnBlur && validate) {
|
|
try {
|
|
setInternal((prev) => ({ ...prev, isValidating: true }));
|
|
errors = await validate(state.values, {});
|
|
} catch (e) {
|
|
console.log("validation", e);
|
|
} finally {
|
|
setInternal((prev) => ({ ...prev, isValidating: false }));
|
|
setState((prev) => ({
|
|
...prev,
|
|
touched: { ...prev.touched, [e.target.name]: validateOnBlur },
|
|
errors,
|
|
}));
|
|
}
|
|
}
|
|
};
|
|
|
|
// Note(Amine): this prop getter will capture the field state
|
|
const getFieldProps = (name, { onChange, onBlur, error } = {}) => ({
|
|
name: name,
|
|
value: state.values[name],
|
|
error: error || state.errors[name],
|
|
touched: state.touched[name],
|
|
onChange: _mergeEventHandlers([onChange, handleFieldChange]),
|
|
onBlur: _mergeEventHandlers([onBlur, handleOnBlur]),
|
|
});
|
|
|
|
/** ---------- NOTE(amine): Form Handlers ---------- */
|
|
|
|
const submitAsync = async () => {
|
|
// NOTE(amine): Don't submit if the form is validating or already submitting
|
|
if (internal.isSubmitting || internal.isValidating) return;
|
|
|
|
//NOTE(amine): touch all inputs
|
|
setState((prev) => {
|
|
const touched = Object.keys(prev.values).reduce((acc, key) => ({ ...acc, [key]: true }), {});
|
|
return { ...prev, touched };
|
|
});
|
|
|
|
// NOTE(amine): validate inputs
|
|
if (validateOnSubmit && validate) {
|
|
let errors = {};
|
|
try {
|
|
setInternal((prev) => ({ ...prev, isValidating: true }));
|
|
errors = await validate(state.values, { ...state.errors });
|
|
if (_hasError(errors)) return;
|
|
} catch (e) {
|
|
console.log("validation", e);
|
|
} finally {
|
|
setInternal((prev) => ({ ...prev, isValidating: false }));
|
|
setState((prev) => ({ ...prev, errors }));
|
|
}
|
|
}
|
|
|
|
// NOTE(amine): submit the form
|
|
if (!onSubmit) return;
|
|
setInternal((prev) => ({ ...prev, isSubmitting: true }));
|
|
try {
|
|
await onSubmit(state.values);
|
|
} catch (e) {
|
|
console.log("submitting", e);
|
|
} finally {
|
|
setInternal((prev) => ({ ...prev, isSubmitting: false }));
|
|
}
|
|
};
|
|
|
|
const handleFormOnSubmit = (e) => {
|
|
e.preventDefault();
|
|
submitAsync()
|
|
.then()
|
|
.catch((e) => console.log(e));
|
|
};
|
|
|
|
// Note(Amine): this prop getter will overide the form onSubmit handler
|
|
const getFormProps = () => ({
|
|
onSubmit: handleFormOnSubmit,
|
|
});
|
|
|
|
return {
|
|
getFieldProps,
|
|
getFormProps,
|
|
values: state.values,
|
|
isSubmitting: internal.isSubmitting,
|
|
isValidating: internal.isValidating,
|
|
};
|
|
};
|
|
|
|
/** NOTE(amine): Since we can use on our design system an input onSubmit,
|
|
* useField is a special case of useForm
|
|
*/
|
|
export const useField = ({
|
|
onSubmit,
|
|
validate,
|
|
initialValue,
|
|
onChange,
|
|
onBlur,
|
|
validateOnBlur = true,
|
|
validateOnSubmit = true,
|
|
}) => {
|
|
const [state, setState] = React.useState({
|
|
isSubmitting: false,
|
|
value: initialValue,
|
|
error: undefined,
|
|
touched: undefined,
|
|
});
|
|
|
|
const _mergeEventHandlers = (events = []) => (e) =>
|
|
events.forEach((event) => {
|
|
if (event) event(e);
|
|
});
|
|
|
|
/** ---------- NOTE(amine): Input Handlers ---------- */
|
|
const handleFieldChange = (e) =>
|
|
setState((prev) => ({
|
|
...prev,
|
|
value: e.target.value,
|
|
error: undefined,
|
|
touched: false,
|
|
}));
|
|
|
|
const handleOnBlur = (e) => {
|
|
// NOTE(amine): validate the inputs onBlur and touch the current input
|
|
let error = {};
|
|
if (validateOnBlur && validate) error = validate(state.value);
|
|
setState((prev) => ({ ...prev, touched: validateOnBlur, error }));
|
|
};
|
|
|
|
const handleFormOnSubmit = (e) => {
|
|
//NOTE(amine): touch all inputs
|
|
setState((prev) => ({ ...prev, touched: true }));
|
|
|
|
// NOTE(amine): validate inputs
|
|
if (validateOnSubmit && validate) {
|
|
const error = validate(state.value);
|
|
setState((prev) => ({ ...prev, error }));
|
|
if (error) return;
|
|
}
|
|
|
|
// NOTE(amine): submit the form
|
|
if (!onSubmit) return;
|
|
setState((prev) => ({ ...prev, isSubmitting: true }));
|
|
onSubmit(state.value)
|
|
.then(() => {
|
|
setState((prev) => ({ ...prev, isSubmitting: false }));
|
|
})
|
|
.catch(() => {
|
|
setState((prev) => ({ ...prev, isSubmitting: false }));
|
|
});
|
|
};
|
|
|
|
// Note(Amine): this prop getter will capture the field state
|
|
const getFieldProps = (name) => ({
|
|
name: name,
|
|
value: state.value,
|
|
error: state.error,
|
|
touched: state.touched,
|
|
onChange: _mergeEventHandlers([onChange, handleFieldChange]),
|
|
onBlur: _mergeEventHandlers([onBlur, handleOnBlur]),
|
|
onSubmit: handleFormOnSubmit,
|
|
});
|
|
|
|
return { getFieldProps, value: state.value, isSubmitting: state.isSubmitting };
|
|
};
|