useEffect如何通過form.getFieldValue(‘xxx‘)監(jiān)聽Form表單變化
場景
子組件中,某一個表格的數據需要依賴于上級組件的某一個表單元素值進行計算。
毫無疑問,首先想到的肯定是監(jiān)聽 form 表單中元素的值,使用 useEffect
監(jiān)聽表單的變化,當值發(fā)生變化時,重新計算渲染。
首先說下我的代碼結構:
Form 表單是一個子組件,表格組件也是一個子組件,且是比較深的子組件(包含在tab標簽頁下)。如果說 Form子組件是一級子組件,那么表格組件就是一個四級子組件。
在這種多層嵌套下,如何把 form 數據傳遞給對應的表格子組件?
毫無疑問,選擇使用:useContext
在頂層組件中通過 Form.useForm()
定義 Form 實例,管理數據狀態(tài),通過 <context.Provider value={}></context.Provider>傳遞,表單子組件和表格子組件都可進行接收。
不過在子組件監(jiān)聽代碼寫好后,卻發(fā)現(xiàn)了一個比較尷尬的問題:
表單元素的值發(fā)生了改變,但子組件的useEffect 卻沒能監(jiān)聽到。
// 子組件 import React, { useContext } from 'react' import context from 'xxx/xxx' const { form } = useContext<any>(context) useEffect(() => { console.log('監(jiān)聽到了變化') // 下面是具體的邏輯 }, [form.getFiledValue('xx')])
注意:
當時令我困惑的是:
當 Form 表單組件的某個表單元素值改變時,雖然子組件 useEffect 沒有監(jiān)聽到變化,但通過表格子組件的輸入框的 onBlur 事件,調用 form.getFiledValue('xx')
,卻可以拿到最新的值。
分析
需求終歸還是要往下開發(fā)的,監(jiān)聽不了,那就想辦法監(jiān)聽!
在項目中,我是使用了 const [form] = Form.useForm();
來創(chuàng)建 Form 實例,管理表單數據狀態(tài),這也是函數式組件推薦的一種方式。
通過使用 useForm ,會讓表單成為 非受控組件,不用通過 useState 創(chuàng)建state 來維護數據。
而且 ant 官網 里面也說了:使用這種方式與其他獲取數據的方式的區(qū)別:
Form 僅會對變更的 Field 進行刷新,從而避免完整的組件刷新可能引發(fā)的性能問題,因而無法在 render 階段通過 form.getFieldsValue
來實時獲取字段值。
看到這里,其實已經很清楚了,因為 使用了 useForm
,導致表單變成了 非受控組件,不能通過 useState 來創(chuàng)建 state 維護數據,只能通過
form.getFieldsValue() / form.setFieldsValue()
來獲取及設置表單數據,ant design 官方文檔也有介紹。所以在這種情況下, 使用 useEffect 監(jiān)聽不到 form 表單的變化。
換句話說:
form.getFieldValue('xxx');
并不是響應式的,由其取到的值,并不會觸發(fā) UI 更新,即:它不是一個 state。
不過,可能有人會問:我之前也使用過 useEffect 來監(jiān)聽 form.getFieldValue(‘xxx’),UI也能正常渲染啊,你這里是不是說錯了?
答: 沒有說錯,我自己之前也有這樣的疑問,也遇到過這類問題。
在實際項目中,一個 react 組件的 re-render 次數是不可控的, 特別是代碼寫的不那么規(guī)范的時候,
所以,當看到 UI 更新的時候,或許是在新的 re-render 中被連帶著更新了;
又或許是項目代碼中 Form組件既使用了 form = {form}
屬性,又使用了 initialValues
屬性。
如下所示:
// const [form] = Form.useForm(); <Form initialValues={formData} form={form} ref={formRef}> </Form>
解決方案
方案1
ant design @4.20 版本推出了一個新特性:Form.useWatch,可以直接獲取 form 中字段對應的值,應該是可以監(jiān)聽到的,具體用法可以參考官網,這里不多做贅述。
可是 4.2 版本后才支持,我的項目版本是 4.12.3,暫不支持這個hooks,部門領導也不讓進行 ant 升級,所以就沒有嘗試這種寫法。
而且使用這種方法,固然可以監(jiān)聽到,不過性能不是太好,不建議使用。
因為它會觸發(fā)整個組件的 re-render,當組件比較大且監(jiān)聽 input 實時輸入時,性能消耗就很恐怖。
方案2
拒絕使用 Form.useForm(),使用 initialValues 屬性,是表單變成一個可控組件,擁有 state,就可以正常監(jiān)聽。
不過對于我的項目代碼來說,改動量太大,不劃算。
方案3
使用 useState
額外定義個 state
,當表單元素值發(fā)生變化時,使用 setState(),然后將其值通過 context.Provider傳遞,在子組件監(jiān)聽。
方案4
使用 useReducer
useReducer 與 useState 都是用來存儲和更新 state,相當于 useState 的升級版。
當 type 數量較多時,建議使用 useReducer
方案5
Ant Plus 5 的 Watch 組件
專門用于監(jiān)聽表單字段變化,并更新局部UI。(沒嘗試過)
總結
其實核心要點就一句話:
Form 內置方法,不是響應式的(即不是一個state),由其設置或者獲取的值,不會觸發(fā)UI更新,只能對變更的field進行刷新。
想要對其進行監(jiān)聽也很簡單,將其變成一個state即可。
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
react清空ant.design中表單內容的方法實現(xiàn)
本文主要介紹了react清空ant.design中表單內容的方法實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-12-12