@preact-signals/query
Version:
A reactive utility for React/Preact that simplifies the handling of data fetching and state management. Powered by Preact Signals, it provides hooks and functions to create reactive resources and manage their state seamlessly.
84 lines (75 loc) • 2.48 kB
text/typescript
import { useComputedOnce } from "@preact-signals/utils/hooks";
import { MutationObserver, MutationObserverResult } from "@tanstack/query-core";
import { useMemo, useState } from "react";
import { useQueryClient$ } from "./react-query/QueryClientProvider";
import {
StaticMutationOptions,
StaticMutationResult,
UseMutationResult$,
} from "./types";
import { useObserverStore } from "./useObserver";
import {
EMPTY_ARRAY,
useRefBasedOptions,
wrapFunctionsInUntracked,
} from "./utils";
import { untracked, useSignalEffect } from "@preact-signals/unified-signals";
import { UseMutateFunction } from "./react-query";
import { shouldThrowError } from "./react-query/utils";
function noop() {}
export const useMutation$ = <
TData = unknown,
TError = unknown,
TVariables = void,
TContext = unknown,
>(
options: () => StaticMutationOptions<TData, TError, TVariables, TContext>
): UseMutationResult$<TData, TError, TVariables, TContext> => {
const $options = useRefBasedOptions(options);
const $client = useQueryClient$({
context: useComputedOnce(() => $options.value.context).value,
});
const observer = useComputedOnce(
// we will update current mutation observer with new options, so using `peek`
() =>
new MutationObserver(
$client.value,
wrapFunctionsInUntracked($options.peek())
)
);
useSignalEffect(() => {
observer.value.setOptions(wrapFunctionsInUntracked($options.value));
});
const mutate: UseMutateFunction<TData, TError, TVariables, TContext> =
useMemo(
() => (variables, mutateOptions) =>
void observer.peek().mutate(variables, mutateOptions).catch(noop),
EMPTY_ARRAY
);
const observerResultToStore = (
result: MutationObserverResult<TData, TError, TVariables, TContext>
) =>
({
...result,
mutate,
mutateAsync: result.mutate,
}) as unknown as StaticMutationResult<TData, TError, TVariables, TContext>;
const store = useObserverStore(() => ({
getCurrent: () => observerResultToStore(observer.value.getCurrentResult()),
subscribe: (emit) =>
observer.value.subscribe((newValue) => {
emit(observerResultToStore(newValue));
}),
}));
const shouldThrow = useComputedOnce(
() =>
store.error &&
shouldThrowError(observer.value.options.useErrorBoundary, [store.error])
);
if (shouldThrow.value) {
untracked(() => {
throw store.error;
});
}
return store;
};