学习Pro-component源码 EditableProTable

行内编辑使用Popver的形式报错

EditTableProTable 报错采用 Popover 的形式去展示,而不是使用默认错误提示理由如下:

保证了单元格不会出现抖动: 单元格占内容有限,默认报错提示会导致单元格发送抖动情况。

在错误内容比较长的情况下甚至会出现折行的情况。

如果最后要有一个保存按钮的话,可以想象点击会破坏整个table结果。

所以从体验感受上来看采用Popver形式报错提示是合适的;看看Popover对这块实现:

EditableProTable 源码实现

pro-components/packages/utils/src/components/InlineErrorFormItem/index.tsx

从GitHub Copilot 看出 实现主要 _internalItemRender 属性用于自定义表单项内容的渲染,包含输入框、错误列表和额外信息。但是它是Form.Item 的内置方式,并且antd源码中注释不可用于任何生产环境。内置方式的问题是提供方不能确保稳定,万一那次更新导致提供的Props下掉了就会导致报错。

ts 复制代码
// 自定义 Form.Item 的渲染结构
_internalItemRender?: {
  mark: string;
  render: (
    props: FormItemInputProps & FormItemInputMiscProps,
    domList: {
      input: JSX.Element;      // 输入控件
      errorList: JSX.Element | null;  // 错误信息列表
      extra: JSX.Element | null;      // 额外信息
    },
  ) => React.ReactNode;
}

自定义实现

所以不能直接抄它源码实现。从效果来看我们实际也不难。我们在Form.Item 上包裹一层Popover组件然后,获取表单error报错信息,然后展示到Popver的 content 上去就可以了,于是会就有👇🏻下面代码:

typescript 复制代码
interface InlineErrorFormItemPopoverProps{
    fieldName: NamePath
    children: React.ReactNode
}


const InlineErrorFormItemPopover<InlineErrorFormItemPopoverProps> = ({children,fieldName})=>{
    const [open,setOpen] = useState(false)
    const form = Form.useFormInstance()
    const errors = form.getFieldError(fieldName) ?? []
    const hasError = errors.length > 0
    
    const handleChange = (changeOpen)=>{
          setOpen(()=> hasError&&ChangeOpen) 
    }
    
    return <Popover open={open} onChange={setOpen} 
            content={<span>{errors.join(',')}</span>}>   
       //  额外加div让其能接受对应onMouseEnter onMouseLeave on Focus onClick点击事件
               <div>
                    {children}
                </div>
           </Popover>

}

// App.tsx
...
    <InlineErrorFormItemPopover>
        <Form.Item name="username" label="UserName" rules={[{ required: true }]}>
            <Input allowclear/> 
        </Form.Item>
    </InlineErrorFormItemPopover>
...

这里装饰器模式的代码很容易想到,但是这种只会在挂载页面时渲染只执行一次,在校验触发报错时不会触发渲染。

所以这里实现关键是 如何在触发报错时,让 InlineErrorFormItemPopover 去渲染获取错误,弹出错误弹框。

Antd Form 提供 shouldUpdate 允许比较表单值来触发重新渲染。可惜shouldUpdate回调里只提供了值做比较,要提供状态做比较就可以一步到位。

这个issue 比较有意思,挂着对提问题人鞭尸😁

javascript 复制代码
 return <Form.Item shouldUpdate={(pre,current)=> pre!==current}>
     {
         ({getFieldError,getFieldName})=>{
               hasError = form.getFieldError(fieldName).length > 0
               const errors = form.getFieldError(fieldName) 
               
               <Popover open={open} onChange={setOpen} content={<span>                                {errors.join(',')}</span>}>   
                   <div>
                       {children}
                    </div>
               </Popover>
         }    
     }
  </Form.Item>

这里每个修改表单值,确实触发了每次对上面代码渲染执行,但是当表单触发校验错误时form.getFieldError(fieldName) 获得的是空数组 ???导致Popver不会弹出错误。

通过查阅antd issue 发现shouldUpdate 如果不设置为true的话,shouldUpdate 下的 form.getFieldError 始终获取为空数组。应该是和校验时机的问题,这个问题出现在2020年,到现在antd也好像没有解决。

所以目前实现只能让 shouldUpdate 设置为true 去解决,但这会导致额外渲染,后续可以关注antd 更新是否解决这个bug。最后实现如下:

typescript 复制代码
import { Form, Popover } from 'antd'
import { NamePath } from 'antd/es/form/interface'
import { FC, useState } from 'react'

interface InlineErrorFormItemPopoverProps {
  fieldName: NamePath
  children: React.ReactNode
}
/**
* @description 行内错误信息展示
*/
const InlineErrorFormItemPopover: FC<InlineErrorFormItemPopoverProps> = ({
  children,
  fieldName,
}) => {
  const [open, setOpen] = useState(false)

  return (
    <Form.Item noStyle shouldUpdate>
      {(form) => {
        const errors = form.getFieldError(fieldName)
        const hasError = errors.length > 0

        return (
          <Popover
            open={hasError && open}
            onOpenChange={setOpen}
            content={
              <span style={{ color: '#ff4d4f' }}>{errors.join(', ')}</span>
            }
            placement="topLeft"
            trigger={['click']}
          >
            <div>{children}</div>
          </Popover>
        )
      }}
    </Form.Item>
  )
}

export default InlineErrorFormItemPopover
相关推荐
saber_andlibert23 分钟前
TCMalloc底层实现
java·前端·网络
逍遥德24 分钟前
如何学编程之01.理论篇.如何通过阅读代码来提高自己的编程能力?
前端·后端·程序人生·重构·软件构建·代码规范
冻感糕人~39 分钟前
【珍藏必备】ReAct框架实战指南:从零开始构建AI智能体,让大模型学会思考与行动
java·前端·人工智能·react.js·大模型·就业·大模型学习
程序员agions42 分钟前
2026年,“配置工程师“终于死绝了
前端·程序人生
alice--小文子1 小时前
cursor-mcp工具使用
java·服务器·前端
晚霞的不甘1 小时前
揭秘 CANN 内存管理:如何让大模型在小设备上“轻装上阵”?
前端·数据库·经验分享·flutter·3d
小迷糊的学习记录1 小时前
0.1 + 0.2 不等于 0.3
前端·javascript·面试
梦帮科技2 小时前
Node.js配置生成器CLI工具开发实战
前端·人工智能·windows·前端框架·node.js·json
VT.馒头2 小时前
【力扣】2695. 包装数组
前端·javascript·算法·leetcode·职场和发展·typescript
css趣多多2 小时前
一个UI内置组件el-scrollbar
前端·javascript·vue.js