基于React 的 AntD 库进行前端开发过程中的问题汇总
背景
最近写了半个月的 React 前端,三年没写过 React 前端了,有些生疏了,汇总一下 基于React 前端的 antD 库编写过程中的低级问题吧。
PS 一下,半个月没有发布博客了,C站产品经理又悄默默地改了样式,博客作为已经二十几年的常用功能,动不动就改 UI ,产品经理怎么想的!
碰到的问题汇总如下:
- Form.Item 嵌套两个表单,第一个表单是 Switch 时,初始值
checked
不生效问题。 - Tree 自定义节点后面的操作通过
titleRender
添加节点的编辑/删除等按钮时,自定义事件和节点本身的选中事件冲突问题。 - Tree 在初始化完成后
defaultExpandAll
不生效问题。 - TreeSelect 组件传递的数据结构不包含默认的 label 和 value值时,需要配置字段映射。
- 构造函数中设置了 state 某个属性a,将该属性传递给子组件后,又在 mounted 的 setState 改属性后子组件获取到仍然是最初绑定的值问题。
- 子组件是用 Function 方式定义的,父组件设置 ref 对象后,通过
refs
取到为空的问题。 - Form 表单设置
initialValues
初始化值后,调用resetFields
不能清空表单的问题。
Switch 初始化不生效问题
出现一个问题,一个表单嵌套的表单中,第一个表单的 Switch 选中时显示第二个表单。但是第一个表单初始化值总是不对。
<Form.Itemlabel="开关状态"name="status"rules={[{ required: true, message: '请选择!' }]}><Switch size="large" checked={this.state.checked} onChange={this.changeChecked}/>{this.state.checked ?<Form.Item name="refId" rules={[{ required: true, message: '请选择数据!' }]}><SelectshowSearchallowClearoptionFilterProp="label"placeholder="请选择"options={this.state.options}/></Form.Item>: null}
在第一个 Switch 的 Form.Item 上设置 valuePropName="checked" initialValue={true}
也无效,折中办法是在 Switch
上设置 checked
属性跟页面某状态绑定,绕过表单。
Tree defaultExpandAll 不生效问题
根据官方说法:在异步加载数据时为何不生效?default 前缀属性只有在初始化时生效,因而异步加载数据时 defaultExpandAll 已经执行完成。
你可以通过受控 expandedKeys 或者在数据加载完成后渲染 Tree 来实现全部展开。
解决办法,设置一个异步加载树数据的标识 isInit,完成后再显示树组件:
{this.state.isInit ?<TreedefaultExpandAll={true}onSelect={this.handleClickTree}treeData={this.state.treeData}titleRender={nodeData => this.titleRender(nodeData)}/>: null
}
Tree 自定义 titleRender 操作事件和点击事件冲突问题
上面的代码中自定义了节点渲染函数,额外添加了节点后的几个操作,如查看/编辑/删除等。但是发现按钮操作时,节点自身的 onSelect
事件也被触发了。
解决办法,渲染事件触发时调用 e.stopPropagation()
阻止事件冒泡。
<Menu onClick={(e) => this.handleBatchClick(e, node)}><Menu.Item key="view" >查看</Menu.Item><Menu.Item key="edit" >编辑</Menu.Item><Popconfirm title={<p style={{lineHeight: '25px'}}>确定删除吗?</p>}onConfirm={(e) => this.delete(e,[node.key])}onCancel={(e) => { e.stopPropagation(); }}}okText="删除"okType="danger" ><Menu.Item key="delete">删除</Menu.Item></Popconfirm>
</Menu>
TreeSelect 组件字段映射配置
Tree 组件的数据的格式「元素结构 {title: "xx", key: "xx", children:[], isLeaf: false}
的数组」和 TreeSelect 的默认数据格式「元素结构是 {label:'', value:'' }
的集合」,两者不一致的,如果两者共用相同的 URL请求,对 TreeSelect 来说存在获取不到选中值的问题。
解决办法,对 TreeSelect
设置 fieldNames 属性完成 label 和 value 的映射。
<TreeSelect treeDefaultExpandAll placeholder={'请选择 '}treeData={this.state.treeData}fieldNames={{label:'title',value:'key'}}/>
setState 修改属性后,子组件 prop 属性通知问题
setState 是异步函数,而子组件渲染时引用的应该是初始化设置的值,如果异步函数中修改了该属性,子组件以及完成渲染了,它获取到的 props 中该属性的值就是最初的值,不能感知到最新的值。
解决办法,各自组件中在 state中维护自身的属性,然后通过父子组件通信完成属性的更新。
- 子组件定义时引用的 props 尽量是已经确定的数据,比如在父类构造函数中定义属性时能直接确定的值,就不要再 componentDidMount 中再通过 setState 设置一次了;否则当状态变更时考虑通知子组件。
- 父组件通过 ref 调用子组件的方法。
- 子组件通过 props 传递的回调函数调用父组件的方法。
基于函数定义的子组件无法通过 Ref对象引用问题
在 React 中,函数组件不能直接接收 ref,因为它们没有实例对象。因此,父组件无法直接通过 ref 获取函数组件的引用。但可以通过以下方式实现类似功能:
✅ 方法一:使用 React.forwardRef 和 useImperativeHandle
这是最推荐的方式,可以确保父组件能够访问子组件的 DOM 元素或方法。
✅ 方法二:使用 ref 回调函数
如果不需要暴露子组件的方法,也可以通过 ref 回调函数间接获取子组件的引用。
✅ 方法三:统一定义为基于Component的组件。
Form 表单初始化和重置问题
Form 表单可以通过 initialValues
初始值,同时表单的 resetFields
是重置为这个初始值的,所以它不能清空表单。
对于返回操作带来的搜索条件,先通过该属性设置初始化值,然后在重置按钮中需要逐个对搜索表单设置为空
handleReset = () => {// 重置操作,是清空整个搜索表单,而 resetFields 是重置为 initialValues 所以需要用 setFieldsValue 逐个重置this.searchFromRef.current.setFieldsValue({c1: '',c2: '',c3: ''});const formData = this.searchFromRef.current.getFieldsValue();this.onFinish(formData);}
启示录
前端编写正反馈还是挺快的,一个增删改查功能的页面能写一周,各种搜索,时间过得也挺快的。除了组件问题外,函数事件,组件定义写写就熟悉了。
有一个感觉就是,mounted 事件动不动就触发了,render 函数不停地被调用。难道一个属性变化,都会触发一次组件重新挂载吗?感觉跟 vue 的不一样呢。