useSubmit
Introduction
This hook mutates data on the server and supports controlling requests. It uses the
Submit Dispatcher
to handle requests and the
Cache
to manage the overall state of the data.
The minimum requirement for useSubmit
is a prepared Request
.
If you intend to retrieve
data from the server, we recommend choosing the
useFetch
hook.
Initialization
const { submit, submitting, onSubmitSuccess, onSubmitError, onSubmitFinished } = useSubmit(postLogin);
How it works?
useSubmit
executes a request when a submit()
function returned from it gets triggered. It uses dependency
tracking to limit re-rendering and improve performance. Under the hood, communication with the core systems is
established by event emitters. Many "helper hooks"
(such as onSubmitSuccess
, onSubmitError
, onSubmitFinished
,
etc.) are returned; these will help handle the request flow and lifecycle. This approach avoids overloading the base
hook with callback logic. It also helps improve code readability, decreases code complication, and promotes more
organized code.
import { useSubmit } from "@hyper-fetch/react";
import { postLogin } from "server";
interface Values {
email: string;
password: string;
}
const LoginPage: React.FC = () => {
const { submit, submitting, onSubmitSuccess, onSubmitError, onSubmitFinished } = useSubmit(postLogin);
onSubmitSuccess(({ response }) => {
console.log(response); // { token: string, refreshToken: string }
});
onSubmitError(({ response }) => {
console.log(response); // { message: string }
});
onSubmitFinished(({ response }) => {
const [payload, error, status] = response;
console.log(payload); // { token: string, refreshToken: string } | null
console.log(error); // { message: string } | null
console.log(status); // 200 / 400 / 404 / 500 ...
});
const onSubmit = (values: Values) => {
submit({ data: values });
};
return (
<Formik initialValues={initialValues} onSubmit={onSubmit} validationSchema={validationSchema}>
<Form>
{error && <Alert severity="error">{error.error_message}</Alert>}
<FormInput name="email" label="Email" placeholder="Fill your email" />
<FormInput type="password" name="password" label="Password" placeholder="Fill your password" />
<Button type="submit" variant="contained" disabled={submitting} className={styles.submit}>
Log in
</Button>
</Form>
</Formik>
);
};
Passing data and params
Data and parameters can be passed in several ways. One option is to use methods on the
Request
, such as setData
or setParams
.
const { submit } = useSubmit(patchUser.setParams({ userId: 1 }).setData({ name: "New Name" }));
However, you may need to pass parameters dynamically, which requires using submit
function options.
const { submit } = useSubmit(patchUser);
const handleSubmit = (id: number, name: string) => {
submit({ data: { name }, params: { userId: id }, queryParams: { search: "test" } });
};
Options
These configuration options should be provided as a second parameter:
const { ... } = useSubmit(request, options)
{
bounce: boolean;
deepCompare: boolean | typeof isEqual;
dependencyTracking: boolean;
disabled: boolean;
initialData: {
...params1: ResponseReturnType<Response, Error, AdapterType>;
...params2: ResponseDetailsType;
cacheTime: number;
clearKey: string;
garbageCollection: number;
}[data] | null;
...params2: {bounceTime:number,bounceType:debounce} | {bounceTime:number,bounceTimeout:number,bounceType:throttle};
}
Returns
Returned values from this hook:
const values = useSubmit(request);
{
additionalData: T extends BaseAdapterType<any, any, any, infer A, any> ? A : never;
data: null | T extends Request<infer D, any, any, any, any, any, any, any, any, any> ? D : never;
error: null | T extends Request<any, any, any, infer G, infer L, any, any, any, any, any> ? G | L : never;
isSuccess: boolean;
retries: number;
status: T extends BaseAdapterType<any, any, infer S, any, any> ? S : never;
timestamp: null | Date;
setAdditionalData: (additionalData: ExtractAdapterAdditionalDataType<ExtractAdapterType<T>>, emitToCache?: boolean) => void;
setData: (data: ExtractResponseType<T>, emitToCache?: boolean) => void;
setError: (error: ExtractErrorType<T>, emitToCache?: boolean) => void;
setIsSuccess: (isSuccess: boolean, emitToCache?: boolean) => void;
setLoading: (loading: boolean, emitToHooks?: boolean) => void;
setRetries: (retries: number, emitToCache?: boolean) => void;
setStatus: (status: ExtractAdapterStatusType<ExtractAdapterType<T>>, emitToCache?: boolean) => void;
setTimestamp: (timestamp: Date, emitToCache?: boolean) => void;
abort: () => void;
bounce: {
active: boolean;
reset: () => void;
};
onSubmitAbort: (callback: OnErrorCallbackType<RequestType>) => void;
onSubmitDownloadProgress: (callback: OnProgressCallbackType) => void;
onSubmitError: (callback: OnErrorCallbackType<RequestType>) => void;
onSubmitFinished: (callback: OnFinishedCallbackType<RequestType>) => void;
onSubmitOfflineError: (callback: OnErrorCallbackType<RequestType>) => void;
onSubmitRequestStart: (callback: OnStartCallbackType<RequestType>) => void;
onSubmitResponseStart: (callback: OnStartCallbackType<RequestType>) => void;
onSubmitSuccess: (callback: OnSuccessCallbackType<RequestType>) => void;
onSubmitUploadProgress: (callback: OnProgressCallbackType) => void;
revalidate: (invalidateKey: InvalidationKeyType | InvalidationKeyType[]) => void;
submit: RequestSendOptionsType<Request>[data] extends NegativeTypes ? (RequestSendOptionsType<Request>[params] extends NegativeTypes ? (options?: RequestSendOptionsType<Request>) => Promise<ResponseReturnType<ExtractResponseType<Request>, ExtractErrorType<Request>, ExtractAdapterType<Request>>> : (options: RequestSendOptionsType<Request>) => Promise<ResponseReturnType<ExtractResponseType<Request>, ExtractErrorType<Request>, ExtractAdapterType<Request>>>) : (options: RequestSendOptionsType<Request>) => Promise<ResponseReturnType<ExtractResponseType<Request>, ExtractErrorType<Request>, ExtractAdapterType<Request>>>;
submitting: boolean;
}