背景
最近寫了半個月的 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 的不一樣呢。