目錄
在前幾篇有提到Controller + control可以幫助你把包裝得很深的組件和react hook form做連結
那是否有Controller + control以外的方法呢🤔
這或許要先了解一下react hook form
的一大重點uncontrolled component
而當表單內的欄位是uncontrolled component時
,會需要透過ref來操作DOM
,進而控制表單
ref
在React18,ref
分為幾種,通常被用在
你想要組件記得一些資料,但是又不想要每次資料更新時組件都會re-render時
- callback ref
組件掛載好後,React會呼叫它,然後取得DOM元素(不需要selector)
react-hook-form的register回傳的ref
就是這種 - ref object
使用createRef、useRef
可以建立ref物件,使用ref.current可以取得值
兩者的差異在於前者被呼叫會回傳一個新的物件(reference不同),後者則是每次渲染都回傳同個物件(reference一樣)
- forwardRef
forwardRef是接收functional component的function,透過它可以將子組件的ref暴露給父組件
常見於深度包裝的組件或HOC
包裝很深的組件怎麼註冊
一些包裝得很深的組件
(ex: MUI的組件,或者自己做的組件)配合react-hook-form使用時,可能會遇到明明畫面上顯示的值改變了,但實際上form的值沒有改變
的問題,這是因為沒有成功把register回傳值傳給組件
根據官方文件,只要正確地暴露ref給上層組件,就能運作
Props Name: ref
Type: React.Ref
Description: Input reference for hook form to register.
所以要做的就是將組件用forwardRef包起來
const Select = forwardRef(
<T extends string>(
{ label, children, ...resetProps }: MuiSelectProps<T>,
ref: ForwardedRef<HTMLInputElement>
) => {
return (
<>
<InputLabel shrink style={{ textAlign: "left" }}>
{label}
</InputLabel>
<MuiSelect ref={ref} {...resetProps}>
{children}
</MuiSelect>
</>
);
}
);
用起來則會像這樣
<Select
label="Education"
displayEmpty
renderValue={(selected) => {
return <Typography>{selected}</Typography>;
}}
{...register("highestEducation")}
>
{educationOptions.map(({ label, value }) => (
<MenuItem value={value} key={value} style={{ textAlign: "left" }}>
{label}
</MenuItem>
))}
</Select>
參考資料
forwardRef
Referencing Values with Refs
A complete guide to React refs
Things you need to know about React ref
React hook form - register