React-admin 4中SimpleForm工具栏无法获取isValid,useFormState报错
react-admin 4版本中,Toolbar的valid、invalid等属性已被移除,转而使用react-hook-form钩子。在使用相关示例代码时,调用useFormState()出现如下报错:
Uncaught TypeError TypeError: Cannot read properties of null (reading 'control')
自定义Toolbar代码
import { Button } from '@mui/material' import React, { memo } from 'react' import { SaveButton, Toolbar, useTranslate } from 'react-admin' import { useFormState } from 'react-hook-form' /** * Save + cancel + delete buttons for a react-admin create/edit form */ const SaveCancelDeleteToolbar = (props: any) => { const { className, handleCancel } = props const translate = useTranslate() const { isValid } = useFormState() return ( <Toolbar className={className}> <SaveButton disabled={!isValid} onClick={handleCancel} /> <Button onClick={handleCancel}>{translate('ra.action.cancel')}</Button> </Toolbar> ) } export default memo(SaveCancelDeleteToolbar)
补充分析
已知通过const {control} = useForm()会创建新表单实例,即便能规避报错,也无法获取当前表单的真实状态。根据useFormState的规则,只有在FormContext作用域内时,useFormState的control参数才是可选的。目前推测问题根源是useFormContext()返回null,导致无法获取表单上下文。
组件调用关系
SimpleForm中调用Toolbar的代码
const AnyForm = (props: CreateEditFormProps) => { const { label1, label2, label3, save, source1, source2, source3, toolbar } = props return ( <SimpleForm mode="onBlur" toolbar={toolbar} onSubmit={save}> <TextInput label={label1} source={source1} validate={[required(), maxLength(50), noEmptySpace]} /> <TextInput label={label2} source={source2} validate={[required(), maxLength(3), minLength(3), noEmptySpace]} /> <TextInput label={label3} source={source3} validate={[required(), maxLength(50), noEmptySpace]} /> </SimpleForm> ) }
Toolbar传递给表单的逻辑
const CreateButtonWithDrawer = (props:any) => { const {allMyProps} = props // Styles, Access, Title, handleCancel functions const memoizedToolbar = useMemo( () => <SaveCancelDeleteToolbar handleCancel={handleCancel} />, [handleCancel] ) return ( <Drawer anchor="right" aria-label={finalTitle} className={classes.drawer} open={isCreating} transitionDuration={drawerTransitionDuration} onClose={modalCancel} > <CreateDrawerContent basepath={basepath} classes={classes} closeDrawer={closeDrawer} pristinecallback={pristineCallback} resourceParam={resource} title={finalTitle} toolbar={memoizedToolbar} onSuccess={onSuccess} > {children} </CreateDrawerContent> </Drawer> ) } const CreateDrawerContent = (props:any) => { const {allMyProps} = props const controllerProps = useCreateController({ mutationOptions: { onSuccess }, redirect: false, resource, }) const { record, save, saving } = controllerProps return ( <CreateContextProvider value={controllerProps}> <ResourceContextProvider value={resource}> {cloneElement(children, { closeDrawer, toolbar, // other props undoable: false, })} </ResourceContextProvider> </CreateContextProvider> ) }
在List操作中调用组件
const AnyListActions = (props: ListActionProps) => { const { referenceRecord, target } = props const memoizedCustomActions = useMemo( () => [ <CreateButtonWithDrawer key="createAction"> <AnyForm referenceRecord={referenceRecord} target={target} /> </CreateButtonWithDrawer>, ], [referenceRecord, target] ) return ( <ListFilterCustomExportActions customActions={memoizedCustomActions} exporter={false} /> ) } export default memo(AnyListActions)
已尝试的无效解决方案
- 更新相关包版本
- 使用FormDataConsumer获取表单值,但需要为每个资源重新编写验证逻辑,无法复用Toolbar
- 调用useCreateContext或useEditContext获取上下文信息,但其中不包含
isValid状态 - 调用useAugmentedForm钩子
- 在SimpleForm中添加validate或resolver属性
- 直接以TSX形式调用Toolbar
- 在子组件中调用Toolbar并设置
toolbar={false}
需求
实现当SimpleForm处于无效状态时,自动禁用SaveButton。
内容的提问来源于stack exchange,提问作者Lucas PARISOT
相关产品推荐
相关产品推荐

