Example: Modify Data
This builds upon what you've learned in the Request Data section.
Creating a Custom Fetch Hook
Let's say you have a form to submit updates on a user object.
Again, we'll need to create a custom hook. This time it will take a FormData object in addition to the id.
import { setupFetch, preparePost } from "@react-nano/use-fetch";
export const useUpdateUser = createFetchHook({
prepare: (init: FetchRequestInit, data: { id: number; formData: FormData }) => {
prepareFormDataPost(init, data.formData);
init.method = "PUT";
return `api/user${data.id}`;
},
getResult: (response: Response) => response.json() as Promise<boolean>,
getError: (response: Response) => response.json() as Promise<RestValidationErrorDTO>,
});
prepareFormDataPost
is a helper function, which will prepare the init object with a FormData object. See helper functions for more details.- Additionally, since
prepareFormDataPost
setsinit.method
to "POST", we override this here with a "PUT". - In this case, we expect the server to return
true
on success, so the result type isboolean
. - Aside from that there is nothing special going on here.
Using the Custom Fetch Hook
Here's how you might use the hook (in addition to useGetUser
from the previous example):
function EditUserComponent(props: { id: number }) {
const [getUser] = useGetUser({ autoSubmit: { id: props.id } });
const [updateUser, submitUpdateUser] = useUpdateUserFetch();
if (getUser.failed) return <div>Error fetching user</div>;
if (!getUser.success) return <div>Loading..</div>;
const user = getUser.data;
const validationErrors = getValidationErrors(updateUser);
return (
<Form
onSubmit={(e) => submitUpdateUser({ id: props.id, formData: new FormData(e.currentTarget) })}
loading={updateUser.loading}
>
<Input name="name" label="Name" placeholder="Name" error={validationErrors.name} defaultValue={user.name} />
...
<ErrorMessageForState state={updateUser} />
<button type="submit">Save</button>
</Form>
);
}
There's a lot more going on here:
- In addition to getting the user, which we already did in the first example,
- We're also using the
useUpdateUserFetch
hook. NoautoSubmit
config means we need to call it manually.- The second entry in the returned array is a submit function, which you can call to manually (re-)submit the request.
- The server returns a validation hashmap in case of an error (implementation is up to you).
- We're using some pseudo UI library to define our user form:
- onSubmit is passed on to the
<form>
element, so we get notified of submits.- On submit, we create a new FormData object from the
<form>
element. - The biggest advantage of this is that you don't need to connect all of your input elements to your components state.
- On submit, we create a new FormData object from the
- When an error happened, we try to show some information about it. See API for more information on the state values.
- onSubmit is passed on to the
In case you are wondering about the implementations of ErrorMessageForState
and getValidationErrors
, here they are:
interface ErrorMessageForStateProps {
state: FetchState<any, RestValidationErrorDTO>;
}
export function ErrorMessageForState({ state }: ErrorMessageForStateProps) {
switch (state.state) {
case "error":
return <div>Error {state.error.error}</div>;
case "exception":
return <div>Error {state.error.message}</div>;
default:
return null;
}
}
export function getValidationErrors(state: FetchState<any, RestValidationErrorDTO>) {
return (state.state === "error" && state.error.validation_errors) || {};
}