一般狀況下,咱們想獲取一個組建或則一個HTML元素的實例經過 Ref特性 就能夠實現,可是某些時候咱們須要在子父級組建中傳遞使用實例,Forwarding Refs提供了一種技術手段來知足這個要求,特別是開發一些重複使用的組建庫時。好比下面的例子:javascript
function MyButton(props) { return ( <button className="MyButton"> {props.children} </button> ); }
上面的代碼中MyButton組件渲染了一個HTML元素。對於使用者而言,React隱藏了將代碼渲染成頁面元素的過程,當其餘組件使用MyButton時,並無任何直接的方法來獲取MyButton中的<button>元素,這樣的設計方法有利於組建的分片管理,下降耦合。java
可是像MyButton這樣的組建,其實僅僅是對基本的HTML元素進行了簡單的封裝。某些時候,上層組建使用他時更但願將其做爲一個基本的HTML元素來看待,實現某些效果須要直接操做DOM,好比focus、selection和animations效果。react
下面的例子將Forwarding Refs添加到MyButton組件中,以實現實例傳遞的效果。app
const MyButton = React.forwardRef((props, ref) => ( <button ref={ref} className="MyButton"> {props.children} </button> )); // 經過ref能夠直接操做<button>元素: const ref = React.createRef(); <MyButton ref={ref}>Click me!</MyButton>;
這個時候,ref能夠直接操做<button>元素。其實執行過程很是簡單,也就下面5步:dom
須要注意的是隻有使用React.forwardRef來建立一個組件時,第二個ref參數纔會存在。固定的方法或者使用類來建立組件並不會接收到ref參數。Forwarding Refs特性並不單單侷限於用在HTML DOM元素上,這種方式也實用於組件之間傳遞Ref。 工具
高階組件(HOCs)僅僅對通常組件的包裝。通常組件被包裝以後對於使用者來講並不清晰其是不是被包裝過,此時使用Ref獲得的是高階組件的實例。所以Forwarding Refs特性對於高階組件來講更有價值。ui
下面是一個高階組件記錄日誌的例子:this
function logProps(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; }
logProps組件用於在每次數據更新先後記錄props中的數據。咱們用其包裝前面的MyButton組件。spa
class MyButton extends React.Component { focus() { // ... } render() { // } } export default logProps(MyButton);
此時經過import並使用Refs實際上獲得的是LogProps的實例:設計
import FancyButton from './FancyButton'; const ref = React.createRef(); <MyButton label="Click Me" handleClick={handleClick} ref={ref} />;
咱們使用Forwarding Refs對高階組件進行簡單的改造便可解決這個問題:
function logProps(Component) { class LogProps extends React.Component { componentDidUpdate(prevProps) { console.log('old props:', prevProps); console.log('new props:', this.props); } render() { const {forwardedRef, ...rest} = this.props; // 經過forwardedRef參數傳遞ref的值 return <Component ref={forwardedRef} {...rest} />; } } //而後使用 React.forwardRef 來包裝建立 LogProps組件的實例 //注意這裏使用 forwardedRef 來傳遞 父組件的 ref // return React.forwardRef((props, ref) => { return <LogProps {...props} forwardedRef={ref} />; }); }
若是咱們不進行任何調整,下面的代碼在調試工具中輸出的組件名稱爲:"ForwardRef(MyComonent)":
const WrappedComponent = React.forwardRef( function myFunction(props, ref) { return <LogProps {...props} forwardedRef={ref} />; } );
能夠經過displayName來設定想要現實的名字:
function logProps(Component) { class LogProps extends React.Component { // ... } //先定義返回的高階組件方法 function forwardRef(props, ref) { return <LogProps {...props} forwardedRef={ref} />; } //而後設定這個組件的名稱 const name = Component.displayName || Component.name; forwardRef.displayName = `logProps(${name})`; //構建組件 return React.forwardRef(forwardRef); }