原文連接:reactjs.org/docs/forwar…html
Ref轉發是一項自動從組件中將ref傳遞給其子組件的技術。但這在絕大部分組件中不是必需的。可是這對某些組件來講會很是有用,尤爲是在某些可複用的第三方組件庫中。常見的場景咱們將在下面的內容描述。react
咱們來考慮下面的例子,FancyButton
組件渲染了一個原生button
DOM元素:數組
function FancyButton(props) {
return (
<button className="FancyButton">
{props.children}
</button>
);
}
複製代碼
React組件隱藏了它們的實現細節,包括它們的渲染輸出。使用FancyButton
的其餘組件一般不須要獲取它內部的button
DOM元素的ref。這是很是好的,由於它避免了組件過於依賴其餘組件的DOM結構。bash
儘管這樣封裝代碼對於應用層級的組件(好比FeedStory
或Comment
)來講是理想的,但這對相似FancyButton
或MyTextInput
這類的高複用性的「葉」組件來講會很是不方便。這些組件會在應用中充當相似原生DOM button
或input
來使用,所以對於管理焦點,選擇或動畫效果來講獲取它們的DOM節點是不可避免的操做。app
Refs轉發是一個選擇性加入的特性,它讓組件接收它們收到的ref並將它傳遞(或稱爲轉發)到更深層級的子元素中。dom
在下面的例子中,FancyButton
使用React.forwardRef
獲取傳遞給它的ref,並轉發給它渲染的button
元素。函數
const FancyButton = React.forwardRef((props, ref) => (
<button ref={ref} className="FancyButton">
{props.children}
</button>
));
// 你如今能夠直接獲取DOM元素的引用了:
const ref = React.createRef();
<FancyButton ref={ref}>Click me!</FancyButton>;
複製代碼
用這種方法,使用了FancyButton
組件的組件就獲得了FancyButton組件中的button
元素的引用而且在必要時可使用它——就像直接操縱DOM元素同樣。動畫
下面是對上面的例子代碼運行的詳細解釋:ui
React.createRef
來建立一個React ref並將它賦值給ref
變量。<FancyButton ref={ref}>
將ref
傳遞給FancyButton
。ref
做爲第二個參數傳遞進forwardRef
內的(props, ref) => ...
函數。<button ref={ref}>
將ref
轉發給button
元素。ref.current
來獲取<button>
DOM節點。注意: 第二個參數ref只存在於當你調用React.forwardRef來定義組件時。正常的函數組件和class組件不會接收ref做爲參數,也沒法在props中獲取到ref。 . ref轉發並不侷限於在DOM元素上使用,你也能夠將ref轉發給一個class組件實例。this
當你在組件庫中使用forwardRef時,你應該把它看成一個破環性的更改而且發佈一個新版本。由於你的組件庫會有和之前的版本顯著的不一樣(好比refs被分配給了誰,導出了聲明類型),這可能會破壞使用者的應用和那些依賴老版本的組件。
處於一樣的緣由,當React,forwardRef存在時有條件地調用它是咱們不推薦的:它改變了你的庫的行爲,並在用戶更新React時會破壞用戶的應用。
轉發refs對高階組件(也被稱爲HOC)十分有用。在下面的例子中咱們使用高階組件在控制檯打印它接收的props:
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
傳遞給它包裹的組件,因此渲染的結果將會是相同的。在下面的例子中,咱們經過這個HOC將全部傳遞給「fancy button」組件的的props都打印出來:
class FancyButton extends React.Component {
focus() {
// ...
}
// ...
}
//咱們經過export LogProps來代替export FancyButton
//這依然會渲染FancyButton
export default logProps(FancyButton);
複製代碼
但在上面的例子中有一點須要注意:refs不會透傳下去,由於ref
不是一個porp。就像key
同樣,React處理它的方式是不一樣的。若是你爲HOC添加了一個ref,那麼這個ref將會成爲最外面的容器組件的引用,而不是被包裹組件的。
這意味着本來打算給FancyButton
使用的ref實際上被綁定在了LogProps
組件上:
import FancyButton from './FancyButton';
const ref = React.createRef();
//咱們引入的FancyButton組件其實是LogProps高階組件
//即便最終的渲染結果是相同的,
//咱們的ref指向的是LogProps而不是內部的FancyButton組件!
//這意味着咱們不能調用ref.current.focus()
<FancyButton
label="Click Me"
handleClick={handleClick}
ref={ref}
/>;
複製代碼
但幸運的是,咱們能夠經過顯式地調用React.forwardRef
API將refs轉發給內部的FancyButton
組件。React.forwardRef
接收一個render函數(接收了props
和ref
做爲參數)並返回一個React節點:
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;
//將自定義的「forwardRef」最爲ref
return <Component ref={forwardedRef} {...rest} />;
}
}
//注意React.forwardRef提供的第二個參數「ref」
//咱們能夠把它做爲一個常規prop傳遞給LogProps,
//好比forwardRef。
//這個forwardRef prop以後將會被Component綁定
return React.forwardRef((props, ref) => {
return <LogProps {...props} forwardedRef={ref} />;
});
}
複製代碼
React.forwardRef
接收一個render函數。React DevTools使用這個函數來決定爲ref轉發組件顯示的內容。
好比下面的例子,組件將會在DevTools中顯示「ForwardRef「:
const WrappedComponent = React.forwardRef((props, ref) => {
return <LogProps {...props} forwardedRef={ref} />;
});
複製代碼
若是你爲render函數起了名字,那麼DevTools中顯示的內容將會包含這個名字(好比:」ForwardRef(myFunction)」):
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} />;
}
//給這個組件一個展現名稱
//使其在DevTools中對用戶更有幫助
//好比 "ForwardRef(logProps(MyComponent))"
const name = Component.displayName || Component.name;
forwardRef.displayName = `logProps(${name})`;
return React.forwardRef(forwardRef);
}
複製代碼