mirror of
https://github.com/spacedriveapp/spacedrive.git
synced 2026-04-19 22:19:49 -04:00
* Add dry_run for `location.create` and `location.addLibrary` * Add dry_run to IndexerRuleCreateArgs - Add invalidate_query to create and edit api method of the location route - Adjust some code to use the new dry_run property * `AddLocationDialog` and `IndexerRuleEditor` now validate with backend without user interaction - Create `useCallbackToWatchForm` to make it easier to watch form changes with an async function that also executes during component mount - `method` is now part of the `addLocationDialog` form schema, and a hidden input - Add an `index.ts` for hooks and components * Fix mobile * Remove redundant type definition * Add `useCallbackToWatchForm` as an additional hook to `react-hooks/exhaustive-deps` - Improve `useCallbackToWatchForm` argument handling, to avoid duplicated references to form * Fix unnecessary form value updates in `addLocationDialog` - Move reset `method` on `path` change logic from the superfluous `useEffect` to `useCallbackToWatchForm` in `addLocationDialog` - Improve core API debug logs for `dry_run` - Fix incorrect file name for `useCallbackToWatchForm` hook - Improve the documentation for `useCallbackToWatchForm` - Fix `useCallbackToWatchForm` not ignoring callback errors - Fix `useCallbackToWatchForm` ignoring returned Promise from `onWatch` - Add basic validation for the `form` argument in `useCallbackToWatchForm` * Remove superflous if checks - generate new core.ts * Remove DryRunError * Remove unused import --------- Co-authored-by: Brendan Allan <brendonovich@outlook.com>
76 lines
2.5 KiB
TypeScript
76 lines
2.5 KiB
TypeScript
import { useCallback, useEffect, useRef } from 'react';
|
|
import { EventType, FieldPath, FieldValues, UseFormReturn } from 'react-hook-form';
|
|
|
|
const noop = () => {};
|
|
|
|
export function useCallbackToWatchForm<S extends FieldValues>(
|
|
callback: (
|
|
value: S,
|
|
info: {
|
|
name?: FieldPath<S>;
|
|
type?: EventType;
|
|
}
|
|
) => void | Promise<void>,
|
|
deps: [UseFormReturn<S, unknown>, ...React.DependencyList]
|
|
): void;
|
|
|
|
export function useCallbackToWatchForm<S extends FieldValues>(
|
|
callback: (
|
|
value: S,
|
|
info: {
|
|
name?: FieldPath<S>;
|
|
type?: EventType;
|
|
}
|
|
) => void | Promise<void>,
|
|
deps: React.DependencyList,
|
|
form: UseFormReturn<S, unknown>
|
|
): void;
|
|
|
|
/**
|
|
* This hook is an async friendly wrapper for useCallback that enables reacting to any form changes.
|
|
*
|
|
* The callback will be called on the first render, and whenever the form changes, with the current
|
|
* form values and the event info regarding the change (empty on first render). If the callback is
|
|
* async, or returns a promise, it will wait for the previous callback to finish before executing
|
|
* the next one. Any errors thrown by the callback will be ignored.
|
|
*
|
|
* @param callback - Callback to be called when form changes
|
|
* @param deps - Dependency list for the callback
|
|
* @param form - Form to watch. If not provided, it will be taken from the first element of the dependency list
|
|
*/
|
|
export function useCallbackToWatchForm<S extends FieldValues>(
|
|
callback: (
|
|
value: S,
|
|
info: {
|
|
name?: FieldPath<S>;
|
|
type?: EventType;
|
|
}
|
|
) => void | Promise<void>,
|
|
deps: React.DependencyList,
|
|
form?: UseFormReturn<S, unknown>
|
|
): void {
|
|
if (form == null) form = deps[0] as UseFormReturn<S, unknown>;
|
|
if (form == null) throw new Error('Form is not provided');
|
|
const { getValues, watch } = form;
|
|
if (typeof getValues !== 'function' || typeof watch !== 'function')
|
|
throw new Error('Form is not provided');
|
|
|
|
// Create a promise chain to make sure async callbacks are called in order
|
|
const chain = useRef<Promise<true | void>>(Promise.resolve(true));
|
|
|
|
// Disable lint warning because this hook is a wrapper for useCallback
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
const onWatch = useCallback(callback, deps);
|
|
|
|
useEffect(() => {
|
|
chain.current = chain.current
|
|
// If this is the first time, we don't need to wait for a form change
|
|
.then((initCheck) => initCheck && onWatch(getValues(), {}))
|
|
.finally(noop);
|
|
|
|
return watch((_, info) => {
|
|
chain.current = chain.current.then(() => onWatch(getValues(), info)).finally(noop);
|
|
}).unsubscribe;
|
|
}, [watch, onWatch, getValues]);
|
|
}
|