forwardRef+useImperativeHandle
- React.forwardRef用法
- useImperativeHandle用法
- 第三個參數的用法
- React.forwardRef與useImperativeHandle配合使用
- 注意事項
React.forwardRef用法
1.創建一個 能夠接受到ref屬性的React 組件。
ref 用來獲取實例,但函數組件不存在實例,所以ref無法獲取函數式組件的實例
React.forwardRef((props, ref) => {子函數組件})
useImperativeHandle用法
- 按需向外暴露成員
- 控制成員暴露的粒度
ref 獲取 DOM 實例,會全面暴露 DOM 實例上的 API,導致外部使用 ref 時自由度太大。使用useImperativeHandle控制 ref 暴露顆粒度,只暴露主要的功能函數。
useImperativeHandle(ref, createHandle, [deps])
useImperativeHandle(ref, () => 自定義ref對象, [依賴項數組])
// 第一個參數為父組件傳遞的 ref
// 第二個參數是一個函數,子組件將自己內部的方法或值作為對象返回,并自動綁定到父組件定義的并傳給子組件的 ref 上
// 第三個參數是函數依賴的值(可選),createHandle中用到的子組件內部定義的變量也需要作為依賴項
第三個參數的用法
- 空數組:只在子組件首次被渲染時,執行 useImperativeHandle 中的 fn 回調,從而把 return 的對象作為父組件接收到的 ref。
- 依賴項數組:子組件首次被渲染時,會依賴項改變時,會執行 useImperativeHandle 中的 fn 回調,從而讓父組件通過 ref 能拿到依賴項的新值。
- 省略依賴項數組(省略第三個參數):此時,子組件內任何 state 的變化,都會導致回調的重新執行,因為每次state改變都會讓函數組件rendered,會重新調用一次回調
React.forwardRef與useImperativeHandle配合使用
React.forwardRef()包裹子組件
子組件中使用useImperativeHandle 向外按需暴露子組件內的成員
父組件中使用childRef.current調用暴露出來的值的方法
<!-- const Child: React.FC = () => { -->
<!-- 被包裝的函數式組件不再是 React.FC 類型,接收兩個參數 props 和轉發過來的 ref -->
const Child = React.forwardRef((props, ref) => { const [count, setCount] = useState(0)const add = (step: number) => {setCount((prev) => (prev += step))}// 1. 向外暴露一個空對象// useImperativeHandle(ref, () => ({}))// 2. 向外暴露一個對象,其中包含了 name 和 age 兩個屬性// useImperativeHandle(ref, () => ({ name: 'liulongbin', age: 22 }))// 3.向外暴露 count 的值和 setCount 函數// useImperativeHandle(ref, () => ({ count, setCount }))// 4.控制成員暴露的粒度,向外暴露reset方法,內部寫死只能設置count為0,外部不能隨意寫入countuseImperativeHandle(ref, () => ({ count, reset:()=>setCount(0) }))return (<><h3>Child 子組件 {count}</h3><button onClick={() => add(-1)}>-1</button><button onClick={() => add(1)}>+1</button></>)
}
當子組件沒有使用useImperativeHandle暴露出自己的任何東西時childRef.current為null
當向外暴露{}時,childRef.current為{}
當向外暴露{ name: ‘liulongbin’, age: 22 }時,childRef.current為{ name: ‘liulongbin’, age: 22 }
export const Father: React.FC = () => {const childRef = useRef<count: number, setCount: (value: number)>()const onShowRef = () => {console.log(childRef.current) }// 重置按鈕的點擊事件處理函數const onReset = () => {// childRef.current?.setCount(0) // 可以設置count為任何值childRef.current?.reset() // 只能設置count為0} return (<><h1>Father 父組件</h1>{/* 點擊按鈕,打印 ref 的值 */}<button onClick={onShowRef}>show Ref</button>{/* 點擊按鈕,重置數據為 0 */}<button onClick={onReset}>重置</button><hr /><Child ref={childRef} /></>)
}
注意事項
1:不要濫用 ref,可以通過 prop 實現,就不應該使用 ref。 僅在你沒法通過 prop 來表達時才使用 ref:例如,滾動到指定節點、聚焦某個節點、觸發一次動畫,以及選擇文本等等。
2:不應該從一個 Model 組件暴露出 {open, close} 這樣的命令式句柄,最好是像 這樣,將 isOpen 作為一個 prop。副作用可以幫你通過 prop 來暴露一些命令式的行為。