grafana/public/app/features/alerting/unified/hooks/useControlledFieldArray.ts
Josh Hunt 3c6e0e8ef8
Chore: ESlint import order (#44959)
* Add and configure eslint-plugin-import

* Fix the lint:ts npm command

* Autofix + prettier all the files

* Manually fix remaining files

* Move jquery code in jest-setup to external file to safely reorder imports

* Resolve issue caused by circular dependencies within Prometheus

* Update .betterer.results

* Fix missing // @ts-ignore

* ignore iconBundle.ts

* Fix missing // @ts-ignore
2022-04-22 14:33:13 +01:00

61 lines
1.8 KiB
TypeScript

import { set } from 'lodash';
import { useCallback } from 'react';
import { UseFormReturn } from 'react-hook-form';
interface Options<R> {
name: string;
formAPI: UseFormReturn<any>;
defaults?: R[];
// if true, sets `__deleted: true` but does not remove item from the array in values
softDelete?: boolean;
}
export type ControlledField<R> = R & {
__deleted?: boolean;
};
const EMPTY_ARRAY = [] as const;
/*
* react-hook-form's own useFieldArray is uncontrolled and super buggy.
* this is a simple controlled version. It's dead simple and more robust at the cost of re-rendering the form
* on every change to the sub forms in the array.
* Warning: you'll have to take care of your own unique identiifer to use as `key` for the ReactNode array.
* Using index will cause problems.
*/
export function useControlledFieldArray<R>(options: Options<R>) {
const { name, formAPI, defaults, softDelete } = options;
const { watch, getValues, reset, setValue } = formAPI;
const fields: Array<ControlledField<R>> = watch(name) ?? defaults ?? EMPTY_ARRAY;
const update = useCallback(
(updateFn: (fields: R[]) => R[]) => {
const values = JSON.parse(JSON.stringify(getValues()));
const newItems = updateFn(fields ?? []);
reset(set(values, name, newItems));
},
[getValues, name, reset, fields]
);
return {
fields,
append: useCallback((values: R) => update((fields) => [...fields, values]), [update]),
remove: useCallback(
(index: number) => {
if (softDelete) {
setValue(`${name}.${index}.__deleted`, true);
} else {
update((items) => {
const newItems = items.slice();
newItems.splice(index, 1);
return newItems;
});
}
},
[update, name, setValue, softDelete]
),
};
}