Skip to content

Commit

Permalink
[GEN-1822]: create a "useGenericForm" hook for form data & error mana…
Browse files Browse the repository at this point in the history
…gement (#1891)

This pull request introduces a new custom hook, `useGenericForm`, and
refactors several existing hooks to utilize it for managing form state
and errors. The main changes include the creation of the
`useGenericForm` hook, updates to existing hooks to use this new hook,
and relevant import adjustments.

### Introduction of `useGenericForm` hook:

*
[`frontend/webapp/hooks/common/useGenericForm.ts`](diffhunk://#diff-33107d1d28564b132423ff2c52fc5c1863e97ff8322a634b67aaee63647f387bR1-R44):
Added a new custom hook `useGenericForm` to handle form state and errors
generically.

### Refactoring existing hooks to use `useGenericForm`:

*
[`frontend/webapp/hooks/actions/useActionFormData.ts`](diffhunk://#diff-034891eac1659418ed1ceccc6d28858abafa1f6f5da688a2550123de5ee99841L1-R3):
Refactored to use `useGenericForm` for managing form state and errors,
replacing local state management.
[[1]](diffhunk://#diff-034891eac1659418ed1ceccc6d28858abafa1f6f5da688a2550123de5ee99841L1-R3)
[[2]](diffhunk://#diff-034891eac1659418ed1ceccc6d28858abafa1f6f5da688a2550123de5ee99841L19-R18)
[[3]](diffhunk://#diff-034891eac1659418ed1ceccc6d28858abafa1f6f5da688a2550123de5ee99841L63-R48)
[[4]](diffhunk://#diff-034891eac1659418ed1ceccc6d28858abafa1f6f5da688a2550123de5ee99841L101-R86)
*
[`frontend/webapp/hooks/destinations/useDestinationFormData.ts`](diffhunk://#diff-74e478b5171cbecdf7abf376e9bc1428c38d585bc6252dc463216f9869bf9f77L5-R5):
Updated to utilize `useGenericForm` for form state and error handling,
removing redundant local state management.
[[1]](diffhunk://#diff-74e478b5171cbecdf7abf376e9bc1428c38d585bc6252dc463216f9869bf9f77L5-R5)
[[2]](diffhunk://#diff-74e478b5171cbecdf7abf376e9bc1428c38d585bc6252dc463216f9869bf9f77L32-R34)
[[3]](diffhunk://#diff-74e478b5171cbecdf7abf376e9bc1428c38d585bc6252dc463216f9869bf9f77L90-L114)
[[4]](diffhunk://#diff-74e478b5171cbecdf7abf376e9bc1428c38d585bc6252dc463216f9869bf9f77L134-R108)
[[5]](diffhunk://#diff-74e478b5171cbecdf7abf376e9bc1428c38d585bc6252dc463216f9869bf9f77L155-R129)
*
[`frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleFormData.ts`](diffhunk://#diff-ac4b5a705bbef372ded4616918885fb1bb8cd2cb8468b7226c89574e709a22f5L1-R3):
Modified to adopt `useGenericForm` for form state and error management,
simplifying the code.
[[1]](diffhunk://#diff-ac4b5a705bbef372ded4616918885fb1bb8cd2cb8468b7226c89574e709a22f5L1-R3)
[[2]](diffhunk://#diff-ac4b5a705bbef372ded4616918885fb1bb8cd2cb8468b7226c89574e709a22f5L23-R22)
[[3]](diffhunk://#diff-ac4b5a705bbef372ded4616918885fb1bb8cd2cb8468b7226c89574e709a22f5L66-R51)
[[4]](diffhunk://#diff-ac4b5a705bbef372ded4616918885fb1bb8cd2cb8468b7226c89574e709a22f5L90-R75)

### Import adjustments:

*
[`frontend/webapp/hooks/common/index.ts`](diffhunk://#diff-e91dfaad8acf2180d9c3e07b98c4f7d5bdab76542b7ab14d5e84202a42aeba29R2):
Exported the new `useGenericForm` hook for use in other parts of the
application.
  • Loading branch information
BenElferink authored Dec 1, 2024
1 parent 21098d7 commit e6bcd4c
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 72 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ interface Props {
formData: DestinationInput;
formErrors: Record<string, string>;
validateForm: () => boolean;
handleFormChange: (key: keyof DestinationInput | string, val: any) => void;
handleFormChange: (key: keyof DestinationInput, val: any) => void;
dynamicFields: DynamicField[];
setDynamicFields: Dispatch<SetStateAction<DynamicField[]>>;
}
Expand Down
25 changes: 5 additions & 20 deletions frontend/webapp/hooks/actions/useActionFormData.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { useState } from 'react';
import { useNotify } from '../notification/useNotify';
import { DrawerBaseItem } from '@/store';
import { ACTION, FORM_ALERTS, NOTIFICATION } from '@/utils';
import { useGenericForm, useNotify } from '@/hooks';
import { FORM_ALERTS, NOTIFICATION } from '@/utils';
import type { ActionDataParsed, ActionInput } from '@/types';

const INITIAL: ActionInput = {
Expand All @@ -16,21 +15,7 @@ const INITIAL: ActionInput = {

export function useActionFormData() {
const notify = useNotify();

const [formData, setFormData] = useState({ ...INITIAL });
const [formErrors, setFormErrors] = useState<Record<string, string>>({});

const handleFormChange = (key: keyof typeof INITIAL, val: any) => {
setFormData((prev) => ({
...prev,
[key]: val,
}));
};

const resetFormData = () => {
setFormData({ ...INITIAL });
setFormErrors({});
};
const { formData, formErrors, handleFormChange, handleErrorChange, resetFormData } = useGenericForm<ActionInput>(INITIAL);

const validateForm = (params?: { withAlert?: boolean; alertTitle?: string }) => {
const errors = {};
Expand Down Expand Up @@ -60,7 +45,7 @@ export function useActionFormData() {
});
}

setFormErrors(errors);
handleErrorChange(undefined, undefined, errors);

return ok;
};
Expand Down Expand Up @@ -98,7 +83,7 @@ export function useActionFormData() {
}
});

setFormData(updatedData);
handleFormChange(undefined, undefined, updatedData);
};

return {
Expand Down
1 change: 1 addition & 0 deletions frontend/webapp/hooks/common/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './useContainerSize';
export * from './useGenericForm';
export * from './useOnClickOutside';
export * from './useKeyDown';
export * from './useTimeAgo';
Expand Down
44 changes: 44 additions & 0 deletions frontend/webapp/hooks/common/useGenericForm.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { useState } from 'react';

export const useGenericForm = <Form = Record<string, any>>(initialFormData: Form) => {
const [formData, setFormData] = useState<Form>({ ...initialFormData });
const [formErrors, setFormErrors] = useState<Partial<Record<keyof Form, string>>>({});

const handleFormChange = (key?: keyof typeof formData, val?: any, obj?: typeof formData) => {
if (!!key) {
// this is for cases where the form contains objects such as "exportedSignals",
// the object's child is targeted with a ".dot" for example: "exportedSignals.logs"

const [parentKey, childKey] = (key as string).split('.');

if (!!childKey) {
setFormData((prev) => ({ ...prev, [parentKey]: { ...prev[parentKey], [childKey]: val } }));
} else {
setFormData((prev) => ({ ...prev, [parentKey]: val }));
}
} else if (!!obj) {
setFormData({ ...obj });
}
};

const handleErrorChange = (key?: keyof typeof formErrors, val?: string, obj?: typeof formErrors) => {
if (!!key) {
setFormErrors((prev) => ({ ...prev, [key]: val }));
} else if (!!obj) {
setFormErrors({ ...obj });
}
};

const resetFormData = () => {
setFormData({ ...initialFormData });
setFormErrors({});
};

return {
formData,
formErrors,
handleFormChange,
handleErrorChange,
resetFormData,
};
};
36 changes: 5 additions & 31 deletions frontend/webapp/hooks/destinations/useDestinationFormData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useState, useEffect } from 'react';
import { DrawerBaseItem } from '@/store';
import { useQuery } from '@apollo/client';
import { GET_DESTINATION_TYPE_DETAILS } from '@/graphql';
import { useConnectDestinationForm, useNotify } from '@/hooks';
import { useConnectDestinationForm, useGenericForm, useNotify } from '@/hooks';
import { ACTION, FORM_ALERTS, NOTIFICATION, safeJsonParse } from '@/utils';
import {
type DynamicField,
Expand All @@ -29,10 +29,9 @@ export function useDestinationFormData(params?: { destinationType?: string; supp
const { destinationType, supportedSignals, preLoadedFields } = params || {};

const notify = useNotify();
const { buildFormDynamicFields } = useConnectDestinationForm();
const { formData, formErrors, handleFormChange, handleErrorChange, resetFormData } = useGenericForm<DestinationInput>(INITIAL);

const [formData, setFormData] = useState({ ...INITIAL });
const [formErrors, setFormErrors] = useState<Record<string, string>>({});
const { buildFormDynamicFields } = useConnectDestinationForm();
const [dynamicFields, setDynamicFields] = useState<DynamicField[]>([]);

const t = destinationType || formData.type;
Expand Down Expand Up @@ -87,31 +86,6 @@ export function useDestinationFormData(params?: { destinationType?: string; supp
});
}, [supportedSignals]);

function handleFormChange(key: keyof typeof INITIAL | string, val: any) {
// this is for a case where "exportedSignals" have been changed, it's an object so they children are targeted as: "exportedSignals.logs"
const [parentKey, childKey] = key.split('.');

if (!!childKey) {
setFormData((prev) => ({
...prev,
[parentKey]: {
...prev[parentKey],
[childKey]: val,
},
}));
} else {
setFormData((prev) => ({
...prev,
[parentKey]: val,
}));
}
}

const resetFormData = () => {
setFormData({ ...INITIAL });
setFormErrors({});
};

const validateForm = (params?: { withAlert?: boolean; alertTitle?: string }) => {
const errors = {};
let ok = true;
Expand All @@ -131,7 +105,7 @@ export function useDestinationFormData(params?: { destinationType?: string; supp
});
}

setFormErrors(errors);
handleErrorChange(undefined, undefined, errors);

return ok;
};
Expand All @@ -152,7 +126,7 @@ export function useDestinationFormData(params?: { destinationType?: string; supp
fields: Object.entries(safeJsonParse(fields, {})).map(([key, value]: [string, string]) => ({ key, value })),
};

setFormData(updatedData);
handleFormChange(undefined, undefined, updatedData);
};

return {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { useState } from 'react';
import { useNotify } from '../notification/useNotify';
import type { DrawerBaseItem } from '@/store';
import { ACTION, FORM_ALERTS, NOTIFICATION } from '@/utils';
import { useGenericForm, useNotify } from '@/hooks';
import { FORM_ALERTS, NOTIFICATION } from '@/utils';
import { PayloadCollectionType, type InstrumentationRuleInput, type InstrumentationRuleSpec } from '@/types';

const INITIAL: InstrumentationRuleInput = {
Expand All @@ -20,21 +19,7 @@ const INITIAL: InstrumentationRuleInput = {

export function useInstrumentationRuleFormData() {
const notify = useNotify();

const [formData, setFormData] = useState({ ...INITIAL });
const [formErrors, setFormErrors] = useState<Record<string, string>>({});

const handleFormChange = (key: keyof typeof INITIAL, val: any) => {
setFormData((prev) => ({
...prev,
[key]: val,
}));
};

const resetFormData = () => {
setFormData({ ...INITIAL });
setFormErrors({});
};
const { formData, formErrors, handleFormChange, handleErrorChange, resetFormData } = useGenericForm<InstrumentationRuleInput>(INITIAL);

const validateForm = (params?: { withAlert?: boolean; alertTitle?: string }) => {
const errors = {};
Expand Down Expand Up @@ -63,7 +48,7 @@ export function useInstrumentationRuleFormData() {
});
}

setFormErrors(errors);
handleErrorChange(undefined, undefined, errors);

return ok;
};
Expand All @@ -87,7 +72,7 @@ export function useInstrumentationRuleFormData() {
};
}

setFormData(updatedData);
handleFormChange(undefined, undefined, updatedData);
};

return {
Expand Down

0 comments on commit e6bcd4c

Please sign in to comment.