[ReactDoc] Forwarding Refs
- 圖片來源:Algolia blog
- 內文資料來源:Forwarding Refs @ React Docs
目的
有些時候父層的元件希望能夠取得子層的 DOM 元素(例如,button
或 input
),以便能夠在父層控制子層 DOM 元素的 focus, selection 或 animation 的效果。這時就可以使用 Ref forwarding 來讓父層取得子層 DOM 元素,以便控制和操作它。
通常需要被 forwardRef 的子 層元件會是封裝好的元件(例如,套件),其他使用它的開發者無法直接修改,因此才需要透過 forwardRef 把控制權交給父層元件,讓其他開發者可以直接控制。
forwardRef 基本使用
舉例來說,我們建立一個 AwesomeInput
元件:
const AwesomeInput = (props) => <input type="text" />;
接著我們在父層 <App>
元件中使用 <AwesomeInput />
元件:
const App = () => {
return <AwesomeInput />;
};
這時候如果想要在 App 元件得到 AwesomeInput 中 <input />
的 value 或者是對它進行 focus 的動作,就需要透過 forwardRef 把這個 <input />
傳到父層以供使用。
要讓 <App />
可以操作到 <AwesomeInput />
中的 <input />
元素,需要:
STEP 1:在父層元件建立 ref
- 先在父層元件透過
useRef
或createRef
建立一個 ref,這裡取名作awesomeInputRef
- 把建立好的
awesomeInputRef
透過ref
屬性傳到<AwesomeInput />
元件內
const App = () => {
const awesomeInputRef = React.useRef(null);
return <AwesomeInput ref={awesomeInputRef} />;
};
STEP 2:在 AwesomeInput 使用 forwardRef
接著在 <AwesomeInput />
元件中,可以使用 React.forwardRef()
來把 <AwesomeInput />
內的 <input />
傳出去:
- 一般的 React Component 是不會取得
ref
的屬性,需要被React.forwardRef()
包起來的才會有ref
屬性:
const AwesomeInput = React.forwardRef((props, ref) => {
return <input type="text" ref={ref} />;
});
STEP 3:在 App 可以使用 AwesomeInput 中的 input DOM 元素
這時候,就可以直接在 App 中操作 AwesomeInput 中的 <input />
元素,舉例來說,我們希望做到 autoFocus 的效果,就可以在 <App />
元件中,透過 awesomeInputRef
取得裡面的 <input />
元素:
const App = () => {
const awesomeInputRef = React.useRef(null);
// App mounted 的時候讓 AwesomeInput 中的 input 元素 focus
React.useEffect(() => {
console.log(awesomeInputRef.current); // <input type="text">...</input>
awesomeInputRef.current.focus(); // 對 AwesomeInput 中的 <input /> 進行操作
}, []);
return <AwesomeInput ref={awesomeInputRef} />;
};
在 HOC 中使用 forwardRef
現在假設建立一個名為 logPropsHOC
的 Higher Order Component(HOC),單純是把元件的 props 有改變時 console 出來的作用:
const logPropsHOC = (WrappedComponent) => {
class LogProps extends React.Component {
componentDidUpdate(prevProps) {
console.log('old props: ', prevProps);
console.log('new props: ', this.props);
}
render() {
return <WrappedComponent {...this.props} />;
}
}
return LogProps;
};
這時候,如果我們把上面寫過的 AwesomeInput 元件用此 HOC 包起來後再使用:
const AwesomeInput = React.forwardRef((props, ref) => {
return <input type="text" ref={ref} />;
});
const AwesomeInputWithLogProps = logPropsHOC(AwesomeInput);