Queries
useQuery and useMutation manage interacting with remote APIs. Their design
is inspired by react-query.
useQuery
Section titled “useQuery”Accepts the following options:
queryFn— an async function that returns the remote dataonStart— runs when the query beginsonSuccess— runs after a successful fetch; receives the dataonError— runs after a failed fetch; receives the erroronEnd— runs when the query ends, regardless of outcomeenabled— boolean (defaulttrue); whether the query runs immediately
Returns a query object with a refetch method. Arguments to refetch are
passed on to queryFn.
function App() { const refs = useRefProxy(); const [, setIsLoading] = usePropertyBoolean(refs.button, 'disabled', true, false); const [, setParagraph] = useTextContent(refs.paragraph);
const { refetch } = useQuery({ queryFn: async () => { const response = await fetch(...); return await response.json(); }, onStart: () => { setParagraph(''); setIsLoading(true); }, onEnd: () => setIsLoading(false), onSuccess: ({ message }) => setParagraph(message), onError: () => setParagraph('Something went wrong'), });
return ( <> <p ref={refs.paragraph} /> <button onClick={refetch} ref={refs.button}>Refetch</button> </> );}useMutation
Section titled “useMutation”Accepts the following options:
mutationFn— an async function that performs the mutationonStart— runs when the mutation beginsonSuccess— runs after a successful mutation; receives the responseonError— runs after a failed mutation; receives the erroronEnd— runs when the mutation ends, regardless of outcome
Returns a mutation object with a mutate method. Arguments to mutate are
passed on to mutationFn.
function App() { const [input, button, paragraph] = useRefs(); const [getInput, setInput] = useTextInput(input); const [, setIsLoading] = usePropertyBoolean(button, 'disabled', true, false); const [, setParagraph] = useTextContent(paragraph);
const { mutate } = useMutation({ mutationFn: () => fetch(...), onStart: () => setIsLoading(true), onEnd: () => setIsLoading(false), onSuccess: () => setParagraph('Saved'), onError: () => setParagraph('Something went wrong'), });
async function handleSubmit(event) { event.preventDefault(); await mutate(getInput()); setInput(''); }
return ( <> <form onSubmit={handleSubmit}> <input ref={input} /> <button ref={button}>Save</button> </form> <p ref={paragraph} /> </> );}Live example
Section titled “Live example” Query + mutation against a fake API
Source
import './examples.css';
import {
useForm,
useMutation,
usePropertyBoolean,
useQuery,
useRefProxy,
useStyleBoolean,
} from 'domstatejsx';
const fakeApi = {
messages: ['a@b.c', 'd@e.f'],
get: async () => {
await new Promise((resolve) => setTimeout(resolve, 400));
return fakeApi.messages;
},
post: async (newMessage) => {
await new Promise((resolve) => setTimeout(resolve, 400));
if (newMessage.indexOf('@') === -1) {
throw new Error('Invalid email address');
}
fakeApi.messages.push(newMessage);
},
};
export default function App() {
const refs = useRefProxy();
const [, setQueryIsLoading] = useStyleBoolean(
refs.loading,
'display',
null,
'none',
);
const [, setFormIsLoading] = usePropertyBoolean(
refs.submit,
'disabled',
true,
false,
);
const { refetch } = useQuery({
onStart: () => setQueryIsLoading(true),
queryFn: fakeApi.get,
onSuccess: (messages) => {
refs.ul.current.replaceChildren(...messages.map((message) => <li>{message}</li>));
},
onEnd: () => setQueryIsLoading(false),
});
const { mutate } = useMutation({
mutationFn: fakeApi.post,
onSuccess: () => {
reset();
refs.ul.current.replaceChildren();
refetch();
},
});
const { registerForm, register, registerError, reset } = useForm({
onStart: async () => {
setFormIsLoading(true);
},
onSubmit: async (data) => {
await mutate(data.message);
},
onEnd: async () => {
setFormIsLoading(false);
},
});
return (
<>
<p class="dx-loading" style={{ display: 'none' }} ref={refs.loading}>
Loading...
</p>
<ul class="dx-list" ref={refs.ul} />
<p>
<button
class="dx-btn"
onClick={() => {
refs.ul.current.replaceChildren();
refetch();
}}
>
Refetch
</button>
</p>
<form {...registerForm()}>
<p>
Message:{' '}
<input class="dx-input" {...register('message', { required: true })} />
</p>
<p class="dx-red" style={{ display: 'none' }} {...registerError()} />
<p class="dx-red" style={{ display: 'none' }} {...registerError('message')} />
<p>
<button class="dx-btn" ref={refs.submit}>
Submit
</button>
</p>
</form>
</>
);
}