refactor(association-field): useAssociationNames hook (#1956)

* chore: useAssociationNames

* refactor: code improve

* refactor: support blocktemplate

* refactor: support blocktemplate

* refactor: useAssociationNames in block template

* fix: cross hierarchical association field layout

* fix: normal field of association field should be append

* refactor: useAssociationNames

* refactor: useCreateActionProps

* refactor: useAssociationNames params

* fix: cleanup

---------

Co-authored-by: chenos <chenlinxh@gmail.com>
This commit is contained in:
katherinehhh 2023-06-07 11:18:35 +08:00 committed by GitHub
parent 83dc81c51b
commit 8809df3f51
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 129 additions and 213 deletions

View File

@ -20,6 +20,9 @@ import { CollectionProvider, useCollection, useCollectionManager } from '../coll
import { FilterBlockRecord } from '../filter-provider/FilterProvider'; import { FilterBlockRecord } from '../filter-provider/FilterProvider';
import { useRecordIndex } from '../record-provider'; import { useRecordIndex } from '../record-provider';
import { SharedFilterProvider } from './SharedFilterProvider'; import { SharedFilterProvider } from './SharedFilterProvider';
import _ from 'lodash';
import { useAssociationNames } from './hooks';
export const BlockResourceContext = createContext(null); export const BlockResourceContext = createContext(null);
export const BlockAssociationContext = createContext(null); export const BlockAssociationContext = createContext(null);
@ -137,7 +140,6 @@ export const useResourceAction = (props, opts = {}) => {
refreshDeps: [runWhenParamsChanged ? null : JSON.stringify(params.appends)], refreshDeps: [runWhenParamsChanged ? null : JSON.stringify(params.appends)],
}, },
); );
// automatic run service when params has changed // automatic run service when params has changed
const firstRun = useRef(false); const firstRun = useRef(false);
useEffect(() => { useEffect(() => {
@ -164,7 +166,7 @@ export const MaybeCollectionProvider = (props) => {
); );
}; };
const BlockRequestProvider = (props) => { export const BlockRequestProvider = (props) => {
const field = useField(); const field = useField();
const resource = useBlockResource(); const resource = useBlockResource();
const [allowedActions, setAllowedActions] = useState({}); const [allowedActions, setAllowedActions] = useState({});
@ -188,7 +190,16 @@ const BlockRequestProvider = (props) => {
const __parent = useContext(BlockRequestContext); const __parent = useContext(BlockRequestContext);
return ( return (
<BlockRequestContext.Provider <BlockRequestContext.Provider
value={{ allowedActions, block: props.block, props, field, service, resource, __parent }} value={{
allowedActions,
block: props.block,
props,
field,
service,
resource,
__parent,
updateAssociationValues: props?.updateAssociationValues || [],
}}
> >
{props.children} {props.children}
</BlockRequestContext.Provider> </BlockRequestContext.Provider>
@ -264,13 +275,21 @@ export const RenderChildrenWithAssociationFilter: React.FC<any> = (props) => {
export const BlockProvider = (props) => { export const BlockProvider = (props) => {
const { collection, association } = props; const { collection, association } = props;
const resource = useResource(props); const resource = useResource(props);
const params = { ...props.params };
const { appends, updateAssociationValues } = useAssociationNames();
if (!Object.keys(params).includes('appends')) {
params['appends'] = appends;
}
return ( return (
<MaybeCollectionProvider collection={collection}> <MaybeCollectionProvider collection={collection}>
<BlockAssociationContext.Provider value={association}> <BlockAssociationContext.Provider value={association}>
<BlockResourceContext.Provider value={resource}> <BlockResourceContext.Provider value={resource}>
<BlockRequestProvider {...props}> <BlockRequestProvider {...props} updateAssociationValues={updateAssociationValues} params={params}>
<SharedFilterProvider {...props}> <SharedFilterProvider {...props} params={params}>
<FilterBlockRecord {...props}>{props.children}</FilterBlockRecord> <FilterBlockRecord {...props} params={params}>
{props.children}
</FilterBlockRecord>
</SharedFilterProvider> </SharedFilterProvider>
</BlockRequestProvider> </BlockRequestProvider>
</BlockResourceContext.Provider> </BlockResourceContext.Provider>

View File

@ -4,7 +4,6 @@ import { Spin } from 'antd';
import React, { createContext, useContext, useEffect, useMemo } from 'react'; import React, { createContext, useContext, useEffect, useMemo } from 'react';
import { RecordProvider } from '../record-provider'; import { RecordProvider } from '../record-provider';
import { BlockProvider, useBlockRequestContext } from './BlockProvider'; import { BlockProvider, useBlockRequestContext } from './BlockProvider';
import { useAssociationNames } from './hooks';
export const DetailsBlockContext = createContext<any>({}); export const DetailsBlockContext = createContext<any>({});
@ -39,15 +38,9 @@ const InternalDetailsBlockProvider = (props) => {
}; };
export const DetailsBlockProvider = (props) => { export const DetailsBlockProvider = (props) => {
const params = { ...props.params };
const { collection } = props;
const { appends } = useAssociationNames(collection);
if (!Object.keys(params).includes('appends')) {
params['appends'] = appends;
}
return ( return (
<BlockProvider {...props} params={params}> <BlockProvider {...props}>
<InternalDetailsBlockProvider {...props} params={params} /> <InternalDetailsBlockProvider {...props} />
</BlockProvider> </BlockProvider>
); );
}; };

View File

@ -7,12 +7,11 @@ import { RecordProvider, useRecord } from '../record-provider';
import { useActionContext, useDesignable } from '../schema-component'; import { useActionContext, useDesignable } from '../schema-component';
import { Templates as DataTemplateSelect } from '../schema-component/antd/form-v2/Templates'; import { Templates as DataTemplateSelect } from '../schema-component/antd/form-v2/Templates';
import { BlockProvider, useBlockRequestContext } from './BlockProvider'; import { BlockProvider, useBlockRequestContext } from './BlockProvider';
import { useAssociationNames } from './hooks';
export const FormBlockContext = createContext<any>({}); export const FormBlockContext = createContext<any>({});
const InternalFormBlockProvider = (props) => { const InternalFormBlockProvider = (props) => {
const { action, readPretty, params, updateAssociationValues } = props; const { action, readPretty, params } = props;
const field = useField(); const field = useField();
const form = useMemo( const form = useMemo(
() => () =>
@ -21,7 +20,7 @@ const InternalFormBlockProvider = (props) => {
}), }),
[], [],
); );
const { resource, service } = useBlockRequestContext(); const { resource, service, updateAssociationValues } = useBlockRequestContext();
const formBlockRef = useRef(); const formBlockRef = useRef();
const record = useRecord(); const record = useRecord();
// if (service.loading) { // if (service.loading) {
@ -68,14 +67,9 @@ export const FormBlockProvider = (props) => {
const record = useRecord(); const record = useRecord();
const { collection } = props; const { collection } = props;
const { __collection } = record; const { __collection } = record;
const params = { ...props.params };
const currentCollection = useCollection(); const currentCollection = useCollection();
const { designable } = useDesignable(); const { designable } = useDesignable();
const isEmptyRecord = useIsEmptyRecord(); const isEmptyRecord = useIsEmptyRecord();
const { appends, updateAssociationValues } = useAssociationNames(collection);
if (!Object.keys(params).includes('appends')) {
params['appends'] = appends;
}
let detailFlag = false; let detailFlag = false;
if (isEmptyRecord) { if (isEmptyRecord) {
detailFlag = true; detailFlag = true;
@ -87,8 +81,8 @@ export const FormBlockProvider = (props) => {
(currentCollection.name === (collection?.name || collection) && !isEmptyRecord) || !currentCollection.name; (currentCollection.name === (collection?.name || collection) && !isEmptyRecord) || !currentCollection.name;
return ( return (
(detailFlag || createFlag) && ( (detailFlag || createFlag) && (
<BlockProvider {...props} block={'form'} params={params}> <BlockProvider {...props} block={'form'}>
<InternalFormBlockProvider {...props} params={params} updateAssociationValues={updateAssociationValues} /> <InternalFormBlockProvider {...props} />
</BlockProvider> </BlockProvider>
) )
); );

View File

@ -1,14 +1,13 @@
import { ArrayField, createForm } from '@formily/core'; import { ArrayField, createForm } from '@formily/core';
import { FormContext, Schema, useField, useFieldSchema } from '@formily/react'; import { FormContext, useField, useFieldSchema } from '@formily/react';
import uniq from 'lodash/uniq';
import React, { createContext, useContext, useEffect, useMemo, useState } from 'react'; import React, { createContext, useContext, useEffect, useMemo, useState } from 'react';
import { useCollection, useCollectionManager } from '../collection-manager'; import { useCollection, useCollectionManager } from '../collection-manager';
import { useFilterBlock } from '../filter-provider/FilterProvider'; import { useFilterBlock } from '../filter-provider/FilterProvider';
import { useRecord } from '../record-provider';
import { FixedBlockWrapper, SchemaComponentOptions, removeNullCondition } from '../schema-component'; import { FixedBlockWrapper, SchemaComponentOptions, removeNullCondition } from '../schema-component';
import { BlockProvider, RenderChildrenWithAssociationFilter, useBlockRequestContext } from './BlockProvider'; import { BlockProvider, RenderChildrenWithAssociationFilter, useBlockRequestContext } from './BlockProvider';
import { mergeFilter } from './SharedFilterProvider'; import { mergeFilter } from './SharedFilterProvider';
import { findFilterTargets } from './hooks'; import { findFilterTargets } from './hooks';
import { useRecord } from '../record-provider';
export const TableBlockContext = createContext<any>({}); export const TableBlockContext = createContext<any>({});
@ -47,62 +46,60 @@ const InternalTableBlockProvider = (props: Props) => {
); );
}; };
const useAssociationNames = (collection) => { // export const useAssociationNames = (collection) => {
const { getCollectionFields } = useCollectionManager(); // const { getCollectionFields } = useCollectionManager();
const collectionFields = getCollectionFields(collection); // const collectionFields = getCollectionFields(collection);
const associationFields = new Set(); // const associationFields = new Set();
for (const collectionField of collectionFields) { // for (const collectionField of collectionFields) {
if (collectionField.target) { // if (collectionField.target) {
associationFields.add(collectionField.name); // associationFields.add(collectionField.name);
const fields = getCollectionFields(collectionField.target); // const fields = getCollectionFields(collectionField.target);
for (const field of fields) { // for (const field of fields) {
if (field.target) { // if (field.target) {
associationFields.add(`${collectionField.name}.${field.name}`); // associationFields.add(`${collectionField.name}.${field.name}`);
} // }
} // }
} // }
} // }
const fieldSchema = useFieldSchema(); // const fieldSchema = useFieldSchema();
const tableSchema = fieldSchema.reduceProperties((buf, schema) => { // const tableSchema = fieldSchema.reduceProperties((buf, schema) => {
if (schema['x-component'] === 'TableV2') { // if (schema['x-component'] === 'TableV2') {
return schema; // return schema;
} // }
if (schema['x-component'] === 'Gantt') { // if (schema['x-component'] === 'Gantt') {
return schema.properties?.table; // return schema.properties?.table;
} // }
return buf; // return buf;
}, new Schema({})); // }, new Schema({}));
return uniq( // return uniq(
tableSchema.reduceProperties((buf, schema) => { // tableSchema.reduceProperties((buf, schema) => {
if (schema['x-component'] === 'TableV2.Column') { // if (schema['x-component'] === 'TableV2.Column') {
const s = schema.reduceProperties((buf, s) => { // const s = schema.reduceProperties((buf, s) => {
const [name] = (s.name as string).split('.'); // const [name] = (s.name as string).split('.');
if (s['x-collection-field'] && associationFields.has(name)) { // if (s['x-collection-field'] && associationFields.has(name)) {
return s; // return s;
} // }
return buf; // return buf;
}, null); // }, null);
if (s) { // if (s) {
// 关联字段和关联的关联字段 // // 关联字段和关联的关联字段
const [firstName] = s.name.split('.'); // const [firstName] = s.name.split('.');
if (associationFields.has(s.name)) { // if (associationFields.has(s.name)) {
buf.push(s.name); // buf.push(s.name);
} else if (associationFields.has(firstName)) { // } else if (associationFields.has(firstName)) {
buf.push(firstName); // buf.push(firstName);
} // }
} // }
} // }
return buf; // return buf;
}, []), // }, []),
); // );
}; // };
export const TableBlockProvider = (props) => { export const TableBlockProvider = (props) => {
const resourceName = props.resource; const resourceName = props.resource;
const params = { ...props.params }; const params = { ...props.params };
const record = useRecord(); const record = useRecord();
const appends = useAssociationNames(props.collection);
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const { getCollection, getCollectionField } = useCollectionManager(); const { getCollection, getCollectionField } = useCollectionManager();
const parent = useCollection(); const parent = useCollection();
@ -131,9 +128,6 @@ export const TableBlockProvider = (props) => {
params['tree'] = true; params['tree'] = true;
} }
} }
if (!Object.keys(params).includes('appends')) {
params['appends'] = appends;
}
const form = useMemo(() => createForm(), [treeTable]); const form = useMemo(() => createForm(), [treeTable]);
return ( return (
<SchemaComponentOptions scope={{ treeTable }}> <SchemaComponentOptions scope={{ treeTable }}>

View File

@ -1,7 +1,7 @@
import { Schema, SchemaExpressionScopeContext, useField, useFieldSchema, useForm } from '@formily/react'; import { SchemaExpressionScopeContext, useField, useFieldSchema, useForm } from '@formily/react';
import { parse } from '@nocobase/utils/client'; import { parse } from '@nocobase/utils/client';
import { Modal, message } from 'antd'; import { Modal, message } from 'antd';
import { cloneDeep, uniq } from 'lodash'; import { cloneDeep } from 'lodash';
import get from 'lodash/get'; import get from 'lodash/get';
import omit from 'lodash/omit'; import omit from 'lodash/omit';
import { ChangeEvent, useContext, useEffect } from 'react'; import { ChangeEvent, useContext, useEffect } from 'react';
@ -16,7 +16,6 @@ import { transformToFilter } from '../../filter-provider/utils';
import { useRecord } from '../../record-provider'; import { useRecord } from '../../record-provider';
import { removeNullCondition, useActionContext, useCompile } from '../../schema-component'; import { removeNullCondition, useActionContext, useCompile } from '../../schema-component';
import { BulkEditFormItemValueType } from '../../schema-initializer/components'; import { BulkEditFormItemValueType } from '../../schema-initializer/components';
import { useSchemaTemplateManager } from '../../schema-templates';
import { useCurrentUserContext } from '../../user'; import { useCurrentUserContext } from '../../user';
import { useBlockRequestContext, useFilterByTk } from '../BlockProvider'; import { useBlockRequestContext, useFilterByTk } from '../BlockProvider';
import { useDetailsBlockContext } from '../DetailsBlockProvider'; import { useDetailsBlockContext } from '../DetailsBlockProvider';
@ -1075,120 +1074,53 @@ export const useAssociationFilterBlockProps = () => {
}; };
}; };
const getTemplateSchema = (schema) => { function getAssociationPath(str) {
const conf = { const lastIndex = str.lastIndexOf('.');
url: `/uiSchemas:getJsonSchema/${schema?.uid}`, if (lastIndex !== -1) {
}; return str.substring(0, lastIndex);
const { data, loading, run } = useRequest(conf, { manual: true });
if (loading) {
} }
useEffect(() => { return str;
if (schema?.uid) { }
run();
}
}, [schema?.uid]);
return schema?.uid ? new Schema(data?.data) : null;
};
export const useAssociationNames = (collection) => { export const useAssociationNames = () => {
const { getCollectionJoinField } = useCollectionManager(); const { getCollectionJoinField } = useCollectionManager();
const { getTemplateById } = useSchemaTemplateManager();
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const associationValues = []; const updateAssociationValues = new Set([]);
const formSchema = fieldSchema.reduceProperties((buf, schema) => { const appends = new Set([]);
if (['FormV2', 'Details', 'List', 'GridCard'].includes(schema['x-component'])) { const getAssociationAppends = (schema, str) => {
return schema; schema.reduceProperties((pre, s) => {
} const prefix = pre || str;
return buf;
}, new Schema({}));
const templateSchema = formSchema.reduceProperties((buf, schema) => {
if (schema['x-component'] === 'BlockTemplate') {
return schema;
}
return buf;
}, null);
const getAssociationAppends = (schema, arr = []) => {
const data = schema.reduceProperties((buf, s) => {
const collectionfield = s['x-collection-field'] && getCollectionJoinField(s['x-collection-field']); const collectionfield = s['x-collection-field'] && getCollectionJoinField(s['x-collection-field']);
if ( const isAssociationSubfield = s.name.includes('.');
collectionfield && const isAssociationField =
['hasOne', 'hasMany', 'belongsTo', 'belongsToMany'].includes(collectionfield.type) && collectionfield && ['hasOne', 'hasMany', 'belongsTo', 'belongsToMany'].includes(collectionfield.type);
s['x-component'] !== 'TableField' if (collectionfield && (isAssociationField || isAssociationSubfield) && s['x-component'] !== 'TableField') {
) { const fieldPath = !isAssociationField && isAssociationSubfield ? getAssociationPath(s.name) : s.name;
buf.push(s.name); const path = prefix === '' || !prefix ? fieldPath : prefix + '.' + fieldPath;
appends.add(path);
if (['Nester', 'SubTable'].includes(s['x-component-props']?.mode)) { if (['Nester', 'SubTable'].includes(s['x-component-props']?.mode)) {
associationValues.push(s.name); updateAssociationValues.add(path);
} const bufPrefix = prefix && prefix !== '' ? prefix + '.' + s.name : s.name;
if (s['x-component-props']?.mode === 'Nester') { getAssociationAppends(s, bufPrefix);
return getAssociationAppends(s, buf);
}
return buf;
} else {
if (s['x-component'] === 'Grid.Row') {
const kk = buf?.concat?.();
return getNesterAppends(s, kk || []);
} else {
return !s['x-component']?.includes('Action.') && s['x-component'] !== 'TableField'
? getAssociationAppends(s, buf)
: buf;
} }
} else if (
![
'ActionBar',
'Action',
'Action.Link',
'Action.Modal',
'Selector',
'Viewer',
'AddNewer',
'AssociationField.Selector',
'AssociationField.AddNewer',
'TableField',
].includes(s['x-component'])
) {
getAssociationAppends(s, str);
} }
}, arr); }, str);
return data || [];
}; };
getAssociationAppends(fieldSchema, '');
function flattenNestedList(nestedList) { return { appends: [...appends], updateAssociationValues: [...updateAssociationValues] };
const flattenedList = [];
function flattenHelper(list, prefix) {
for (let i = 0; i < list.length; i++) {
if (Array.isArray(list[i])) {
`${prefix}` !== `${list[i][0]}` && flattenHelper(list[i], `${prefix}.${list[i][0]}`);
} else {
const searchTerm = `.${list[i]}`;
const lastIndex = prefix.lastIndexOf(searchTerm);
let str = '';
if (lastIndex !== -1) {
str = prefix.slice(0, lastIndex) + prefix.slice(lastIndex + searchTerm.length);
}
if (!str) {
!list.includes(str) && flattenedList.push(`${list[i]}`);
} else {
!list.includes(str) ? flattenedList.push(`${str}.${list[i]}`) : flattenedList.push(str);
}
}
}
}
for (let i = 0; i < nestedList.length; i++) {
flattenHelper(nestedList[i], nestedList[i][0]);
}
return uniq(flattenedList.filter((obj) => !obj?.startsWith('.')));
}
const getNesterAppends = (gridSchema, data) => {
gridSchema.reduceProperties((buf, s) => {
buf.push(getAssociationAppends(s));
return buf;
}, data);
return data.filter((g) => g.length);
};
const template = getTemplateById(templateSchema?.['x-component-props']?.templateId);
const schema = getTemplateSchema(template);
if (schema) {
const associations = getAssociationAppends(schema);
const appends = flattenNestedList(associations);
return {
appends,
updateAssociationValues: appends.filter((item) => associationValues.some((suffix) => item.endsWith(suffix))),
};
}
if (!schema) {
const associations = getAssociationAppends(formSchema);
const appends = flattenNestedList(associations);
return {
appends,
updateAssociationValues: appends.filter((item) => associationValues.some((suffix) => item.endsWith(suffix))),
};
}
}; };

View File

@ -2,9 +2,9 @@ import { LoadingOutlined } from '@ant-design/icons';
import { RecursionField, connect, mapProps, observer, useField, useFieldSchema } from '@formily/react'; import { RecursionField, connect, mapProps, observer, useField, useFieldSchema } from '@formily/react';
import { Input } from 'antd'; import { Input } from 'antd';
import React from 'react'; import React from 'react';
import { RecordProvider } from '../../../';
import { RemoteSelect, RemoteSelectProps } from '../remote-select'; import { RemoteSelect, RemoteSelectProps } from '../remote-select';
import useServiceOptions from './hooks'; import useServiceOptions from './hooks';
import { RecordProvider } from '../../../';
export type AssociationSelectProps<P = any> = RemoteSelectProps<P> & { export type AssociationSelectProps<P = any> = RemoteSelectProps<P> & {
action?: string; action?: string;

View File

@ -4,8 +4,6 @@ import React, { createContext, useContext, useEffect, useMemo, useState } from '
import { BlockProvider, useBlockRequestContext } from '../../../block-provider'; import { BlockProvider, useBlockRequestContext } from '../../../block-provider';
import { FormLayout } from '@formily/antd'; import { FormLayout } from '@formily/antd';
import { css } from '@emotion/css'; import { css } from '@emotion/css';
import { useAssociationNames } from '../../../block-provider/hooks';
export const GridCardBlockContext = createContext<any>({}); export const GridCardBlockContext = createContext<any>({});
const InternalGridCardBlockProvider = (props) => { const InternalGridCardBlockProvider = (props) => {
@ -57,15 +55,8 @@ const InternalGridCardBlockProvider = (props) => {
}; };
export const GridCardBlockProvider = (props) => { export const GridCardBlockProvider = (props) => {
const params = { ...props.params };
const { collection } = props;
const { appends } = useAssociationNames(collection);
if (!Object.keys(params).includes('appends')) {
params['appends'] = appends;
}
return ( return (
<BlockProvider {...props} params={params}> <BlockProvider {...props}>
<InternalGridCardBlockProvider {...props} /> <InternalGridCardBlockProvider {...props} />
</BlockProvider> </BlockProvider>
); );

View File

@ -2,7 +2,6 @@ import { createForm } from '@formily/core';
import { FormContext, useField } from '@formily/react'; import { FormContext, useField } from '@formily/react';
import React, { createContext, useContext, useEffect, useMemo } from 'react'; import React, { createContext, useContext, useEffect, useMemo } from 'react';
import { FormLayout } from '@formily/antd'; import { FormLayout } from '@formily/antd';
import { useAssociationNames } from '../../../block-provider/hooks';
import { BlockProvider, useBlockRequestContext } from '../../../block-provider'; import { BlockProvider, useBlockRequestContext } from '../../../block-provider';
export const ListBlockContext = createContext<any>({}); export const ListBlockContext = createContext<any>({});
@ -38,15 +37,8 @@ const InternalListBlockProvider = (props) => {
}; };
export const ListBlockProvider = (props) => { export const ListBlockProvider = (props) => {
const params = { ...props.params };
const { collection } = props;
const { appends } = useAssociationNames(collection);
if (!Object.keys(params).includes('appends')) {
params['appends'] = appends;
}
return ( return (
<BlockProvider {...props} params={params}> <BlockProvider {...props}>
<InternalListBlockProvider {...props} /> <InternalListBlockProvider {...props} />
</BlockProvider> </BlockProvider>
); );

View File

@ -21,6 +21,7 @@ export const BlockTemplate = observer(
const template = useMemo(() => getTemplateById(templateId), [templateId]); const template = useMemo(() => getTemplateById(templateId), [templateId]);
const onSuccess = (data) => { const onSuccess = (data) => {
fieldSchema['x-linkage-rules'] = data?.data?.['x-linkage-rules'] || []; fieldSchema['x-linkage-rules'] = data?.data?.['x-linkage-rules'] || [];
fieldSchema.setProperties(data?.data?.properties);
}; };
return template ? ( return template ? (
<BlockTemplateContext.Provider value={{ dn, field, fieldSchema, template }}> <BlockTemplateContext.Provider value={{ dn, field, fieldSchema, template }}>

View File

@ -18,7 +18,7 @@ export function FormBlockProvider(props) {
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const field = useField(); const field = useField();
const formBlockRef = useRef(null); const formBlockRef = useRef(null);
const { appends, updateAssociationValues } = useAssociationNames(props.collection); const { appends, updateAssociationValues } = useAssociationNames();
const [formKey] = Object.keys(fieldSchema.toJSON().properties ?? {}); const [formKey] = Object.keys(fieldSchema.toJSON().properties ?? {});
const values = userJob?.result?.[formKey]; const values = userJob?.result?.[formKey];