swr
Version:
React Hooks library for remote data fetching
126 lines (123 loc) • 4.92 kB
JavaScript
import { useRef, useCallback } from 'react';
import useSWR, { useSWRConfig } from 'swr';
import { withMiddleware, useStateWithDeps, UNDEFINED, serialize, mergeObjects, getTimestamp, useIsomorphicLayoutEffect } from 'swr/_internal';
const mutation = ()=>{
return (key, fetcher, config = {})=>{
const { mutate } = useSWRConfig();
const keyRef = useRef(key);
const fetcherRef = useRef(fetcher);
const configRef = useRef(config);
// Ditch all mutation results that happened earlier than this timestamp.
const ditchMutationsUntilRef = useRef(0);
const [stateRef, stateDependencies, setState] = useStateWithDeps({
data: UNDEFINED,
error: UNDEFINED,
isMutating: false
});
const currentState = stateRef.current;
const trigger = useCallback(async (arg, opts)=>{
const [serializedKey, resolvedKey] = serialize(keyRef.current);
if (!fetcherRef.current) {
throw new Error('Can’t trigger the mutation: missing fetcher.');
}
if (!serializedKey) {
throw new Error('Can’t trigger the mutation: missing key.');
}
// Disable cache population by default.
const options = mergeObjects(mergeObjects({
populateCache: false,
throwOnError: true
}, configRef.current), opts);
// Trigger a mutation, and also track the timestamp. Any mutation that happened
// earlier this timestamp should be ignored.
const mutationStartedAt = getTimestamp();
ditchMutationsUntilRef.current = mutationStartedAt;
setState({
isMutating: true
});
try {
const data = await mutate(serializedKey, fetcherRef.current(resolvedKey, {
arg
}), // We must throw the error here so we can catch and update the states.
mergeObjects(options, {
throwOnError: true
}));
// If it's reset after the mutation, we don't broadcast any state change.
if (ditchMutationsUntilRef.current <= mutationStartedAt) {
setState({
data,
isMutating: false,
error: undefined
});
options.onSuccess == null ? void 0 : options.onSuccess(data, serializedKey, options);
}
return data;
} catch (error) {
// If it's reset after the mutation, we don't broadcast any state change
// or throw because it's discarded.
if (ditchMutationsUntilRef.current <= mutationStartedAt) {
setState({
error: error,
isMutating: false
});
options.onError == null ? void 0 : options.onError(error, serializedKey, options);
if (options.throwOnError) {
throw error;
}
}
}
}, // eslint-disable-next-line react-hooks/exhaustive-deps
[]);
const reset = useCallback(()=>{
ditchMutationsUntilRef.current = getTimestamp();
setState({
data: UNDEFINED,
error: UNDEFINED,
isMutating: false
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useIsomorphicLayoutEffect(()=>{
keyRef.current = key;
fetcherRef.current = fetcher;
configRef.current = config;
});
// We don't return `mutate` here as it can be pretty confusing (e.g. people
// calling `mutate` but they actually mean `trigger`).
// And also, `mutate` relies on the useSWR hook to exist too.
return {
trigger,
reset,
get data () {
stateDependencies.data = true;
return currentState.data;
},
get error () {
stateDependencies.error = true;
return currentState.error;
},
get isMutating () {
stateDependencies.isMutating = true;
return currentState.isMutating;
}
};
};
};
/**
* A hook to define and manually trigger remote mutations like POST, PUT, DELETE and PATCH use cases.
*
* @link https://swr.vercel.app/docs/mutation
* @example
* ```jsx
* import useSWRMutation from 'swr/mutation'
*
* const {
* data,
* error,
* trigger,
* reset,
* isMutating
* } = useSWRMutation(key, fetcher, options?)
* ```
*/ var index = withMiddleware(useSWR, mutation);
export { index as default };