97 lines
2.8 KiB
JavaScript
97 lines
2.8 KiB
JavaScript
import * as React from 'react';
|
|
import { useFormContext } from 'react-hook-form';
|
|
import set from 'lodash/set';
|
|
import isEqual from 'lodash/isEqual';
|
|
import { useQuery } from '@tanstack/react-query';
|
|
|
|
import api from 'helpers/api';
|
|
|
|
const variableRegExp = /({.*?})/;
|
|
// TODO: extract this function to a separate file
|
|
function computeArguments(args, getValues) {
|
|
const initialValue = {};
|
|
|
|
return args.reduce((result, { name, value }) => {
|
|
const isVariable = variableRegExp.test(value);
|
|
|
|
if (isVariable) {
|
|
const sanitizedFieldPath = value.replace(/{|}/g, '');
|
|
const computedValue = getValues(sanitizedFieldPath);
|
|
|
|
if (computedValue === undefined || computedValue === '')
|
|
throw new Error(`The ${sanitizedFieldPath} field is required.`);
|
|
|
|
set(result, name, computedValue);
|
|
return result;
|
|
}
|
|
|
|
set(result, name, value);
|
|
return result;
|
|
}, initialValue);
|
|
}
|
|
|
|
/**
|
|
* Fetch the dynamic fields for the given step.
|
|
* This hook must be within a react-hook-form context.
|
|
*
|
|
* @param stepId - the id of the step
|
|
* @param schema - the field schema that needs the dynamic fields
|
|
*/
|
|
function useDynamicFields(stepId, schema) {
|
|
const lastComputedVariables = React.useRef({});
|
|
const { getValues } = useFormContext();
|
|
const formValues = getValues();
|
|
|
|
/**
|
|
* Return `null` when even a field is missing value.
|
|
*
|
|
* This must return the same reference if no computed variable is changed.
|
|
* Otherwise, it causes redundant network request!
|
|
*/
|
|
const computedVariables = React.useMemo(() => {
|
|
if (schema.type === 'dropdown' && schema.additionalFields) {
|
|
try {
|
|
const variables = computeArguments(
|
|
schema.additionalFields.arguments,
|
|
getValues,
|
|
);
|
|
// if computed variables are the same, return the last computed variables.
|
|
if (isEqual(variables, lastComputedVariables.current)) {
|
|
return lastComputedVariables.current;
|
|
}
|
|
lastComputedVariables.current = variables;
|
|
return variables;
|
|
} catch (err) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
/**
|
|
* `formValues` is to trigger recomputation when form is updated.
|
|
* `getValues` is for convenience as it supports paths for fields like `getValues('foo.bar.baz')`.
|
|
*/
|
|
}, [schema, formValues, getValues]);
|
|
|
|
const query = useQuery({
|
|
queryKey: ['dynamicFields', stepId, computedVariables],
|
|
queryFn: async ({ signal }) => {
|
|
const { data } = await api.post(
|
|
`/v1/steps/${stepId}/dynamic-fields`,
|
|
{
|
|
dynamicFieldsKey: computedVariables.key,
|
|
parameters: computedVariables.parameters,
|
|
},
|
|
{ signal },
|
|
);
|
|
|
|
return data;
|
|
},
|
|
enabled: !!stepId && !!computedVariables,
|
|
});
|
|
|
|
return query;
|
|
}
|
|
|
|
export default useDynamicFields;
|