原文: Fullstack React's Guide to using Refs in React Components
做者:Yomi Eluwande
譯者:博軒
使用 React
時,咱們的默認思惟方式應該是 不會強制修改 DOM
,而是經過傳入 props
從新渲染組件。可是,有些狀況卻沒法避免修改 DOM
。javascript
React
中的 Refs
提供了一種訪問 render()
方法中建立的 React
元素(或 DOM
節點)的方法。css
當父組件須要與子組件交互時,咱們一般使用 props 來傳遞相關信息。 可是,在某些狀況下,咱們可能須要修改子項,而不用新的props
從新呈現 (re-rendering) 它。 這時候就須要 refs
出場了。html
咱們建議在如下狀況下使用 refs
:java
DOM
庫集成譯註:第三點是否也能夠理解爲使用event
對象呢?在 React 中就是合成事件(SyntheticEvent)。
官方文檔中提到:避免使用refs
來作任何能夠經過聲明式實現來完成的事情。
因此一旦咱們肯定咱們真的應該使用 refs
,咱們須要如何使用它們呢?node
您能夠經過多種方式使用 refs
:react
refs
(Forwarding refs)接下來,讓咱們看看每一種實現方式:typescript
可使用該 React.createRef()
函數建立 Refs
,並經過該 ref
屬性附加到 React
組件中的 HTML
元素。segmentfault
一般在組件的構造函數內建立 ref
,使其在整個組件中可用。例如:數組
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.firstRef = React.createRef();
}
render() {
return <div ref={this.firstRef} />;
}
}複製代碼
如上所示:瀏覽器
ref
實例在構造函數中建立,並賦值給 this.firstRef
render()
方法內部,將構造函數中建立的 ref
傳遞給 div
接下來,讓咱們看一個在 React
組件中使用 refs
的示例。
這是另外一個例子:
// Ref.js
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
// create a ref to store the textInput DOM element
this.textInput = React.createRef();
this.focusTextInput = this.focusTextInput.bind(this);
}
focusTextInput() {
// Explicitly focus the text input using the raw DOM API
// Note: we're accessing "current" to get the DOM node
this.textInput.current.focus();
}
render() {
// tell React that we want to associate the <input> ref
// with the `textInput` that we created in the constructor
return (
<div>
<input type="text" ref={this.textInput} />
<input
type="button"
value="Focus the text input"
onClick={this.focusTextInput}
/>
</div>
);
}
}複製代碼
在上面的代碼塊中,咱們構建了一個按鈕,當單擊它時,該頁面會自動聚焦在輸入框上。
首先,咱們在構造方法中建立一個 ref
實例,並將其賦值給 this.textInput
,而後經過 ref
屬性將其分配給 input
元素。
<input type="text" ref={this.textInput} />複製代碼
注意,當 ref
屬性被一個 HTML
元素使用時(好比當前示例中的 input
元素),在 constructor
中使用React.createRef()
建立的 ref
會接收來自底層 DOM
元素的 current
值。
譯註:這裏的 current 應該是 合成事件(SyntheticEvent)
這意味着訪問 DOM
值,咱們須要寫這樣的東西:
this.textInput.current;複製代碼
第二個元素是一個按鈕,點擊它以後會自動聚焦到第一個輸入框上面。咱們爲 onClick
屬性設置了 this.focusTextInput
函數。
<input
type="button"
value="Focus the text input"
onClick={this.focusTextInput}
/>複製代碼
函數 focusTextInput()
使用了 JavaScript
構建 DOM
的標準函數。 .focus()
方法會將光標聚焦於文本輸入框上。
focusTextInput() {
this.textInput.current.focus();
}複製代碼
最後,focusTextInput
函數綁定在這樣的 constructor
方法中的:
this.focusTextInput = this.focusTextInput.bind(this);複製代碼
在這個例子中,咱們將看到如何爲 input
輸入框設置 ref
屬性,並經過 ref
來獲取值。示例以下:
在這個例子中,咱們建立了一個 input
輸入框來輸入值。而後,當單擊提交按鈕時,咱們將讀取此值,並在控制檯打印。
// Ref.js
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
// create a ref to store the textInput DOM element
this.textInput = React.createRef();
}
handleSubmit = e => {
e.preventDefault();
console.log(this.textInput.current.value);
};
render() {
// tell React that we want to associate the <input> ref
// with the `textInput` that we created in the constructor
return (
<div> <form onSubmit={e => this.handleSubmit(e)}> <input type="text" ref={this.textInput} /> <button>Submit</button> </form> </div> ); } }複製代碼
一樣,咱們使用該 React.createRef()
函數建立一個 ref
實例,而後將它分配給實例變量 this.textInput
。
在 render
函數中,咱們但願讀取 form
下輸入框的值。咱們如何讀取這個值? 經過爲 input
指定一個 ref
,而後讀取 ref
的值。
<input type="text" ref={this.textInput} />複製代碼
點擊提交按鈕,上面示例中 form
元素會經過 onSubmit
方法,調用 this.handleSubmit
函數 ,並在控制檯打印輸入框中的信息。
handleSubmit = e => {
e.preventDefault();
console.log(this.textInput);
};複製代碼
上面,參數e
包含事件對象。咱們使用e.preventDefault()
來告訴瀏覽器咱們正在處理被點擊的提交按鈕,咱們不但願這個事件「冒泡」(意思就是說,阻止瀏覽器的默認行爲)。譯註:這裏能夠看一下 React 對於事件的處理:在 React 中另外一個不一樣點是你不能經過返回 false 的方式阻止默認行爲。你必須顯式的使用
preventDefault
在上面示例中,咱們打印了 this.textInput
,在控制檯能夠看到一個 ref
對象。
> Object {current: HTMLInputElement}複製代碼
請注意,它有一個 current
屬性,即 HTMLInputElement
。這是 input
DOM 元素自己,而不是實際值。 咱們必須使用 this.textInput.current.value
來獲取 input
標籤的實際值:
handleSubmit = e => {
e.preventDefault();
console.log(this.textInput.current.value);
};複製代碼
使用 refs
是一種從表單中直接提取值的方式:只須要給 input
標籤設置 ref
,並在你須要的時候將值提取出來。
Refs 回調 是在 React
中使用 ref
的另外一種方式。要以這種方式使用 ref
,咱們須要爲 ref
屬性設置回調函數。當咱們設置 ref
時,React
會調用這個函數,並將 element
做爲第一個參數傳遞給它。
這是另外一個例子的代碼。像上面的示例同樣,此代碼獲取 input
標籤的文本值,但在這裏咱們使用回調引用:
// Refs.js
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
this.textInput = null;
this.setTextInputRef = element => {
this.textInput = element;
};
}
handleSubmit = e => {
e.preventDefault();
console.log(this.textInput.value);
};
render() {
return (
<div> <form onSubmit={e => this.handleSubmit(e)}> <input type="text" ref={this.setTextInputRef} /> <button>Submit</button> </form> </div> ); } }複製代碼
上面的示例中,咱們將 input
標籤的 ref
設置爲 this.setTextInputRef
。
當組件安裝時,React
會將 DOM
元素傳遞給 ref
的回調;當組件卸載時,則會傳遞 null
。(ref
回調會在 componentDidMount
和 componentDidUpdate
生命週期以前調用。)
還有另外一種設置 refs
的方法,但它被認爲是過期的,可能很快就會被棄用。可是你可能會在其餘人的代碼中看到它,因此這裏說一下。
使用 string refs
,你將會看到這樣的 input
標籤:
<input type="text" ref="textInput" />複製代碼
而後,咱們能夠在組建上獲得這樣的值:this.refs.textInput.value
- 可是,再次聲明,這不該該在新代碼中使用,由於這個 API
將被棄用。
Ref forwarding
是一種將 ref
經過組件傳遞給其子節點的技術。它對於可複用組件庫和高階組件(HOC)等狀況很是有用。
您可使用 React.forwardRef
函數將 ref
轉發到組件。咱們來看下面的例子:
// Ref.js
const TextInput = React.forwardRef((props, ref) => (
<input type="text" placeholder="Hello World" ref={ref} />
));
const inputRef = React.createRef();
class CustomTextInput extends React.Component {
handleSubmit = e => {
e.preventDefault();
console.log(inputRef.current.value);
};
render() {
return (
<div>
<form onSubmit={e => this.handleSubmit(e)}>
<TextInput ref={inputRef} />
<button>Submit</button>
</form>
</div>
);
}
}複製代碼
Ref forwarding
容許組件接收一個 ref
,並將它向下傳遞(換句話說,「轉發」它)給子組件。
在上面的示例中,咱們使用 input
標籤建立了一個名爲 TextInput
的組件。那麼,咱們如何將 ref
傳遞或轉發到 input
標籤呢?
首先,咱們使用下面的代碼建立一個 ref
:
const inputRef = React.createRef();複製代碼
而後,咱們將 ref
經過爲組件 <TextInput ref={inputRef}>
指定一個同名的 JSX
的屬性,將 ref
向下傳遞。而後 React
將會把 ref
做爲第二個參數轉發給 forwardRef
函數。
接下來,咱們將此 ref
參數轉發給 <input ref={ref}>
。如今能夠在外層組件經過 inputRef.current
訪問DOM節點的值了。
最後,讓咱們看一下使用 refs
的另外一個例子,但此次是使用高階組件(HOC)。
在上面的示例應用程序中,會將全部 input
標籤中輸入的值在控制檯打印。這裏已經爲 input
標籤設置了 ref
屬性,接下來,讓咱們看一下須要如何在高階組件中傳遞 / 轉發 ref
。
const Input = InputComponent => {
const forwardRef = (props, ref) => {
const onType = () => console.log(ref.current.value);
return <InputComponent forwardedRef={ref} onChange={onType} {...props} />; }; return React.forwardRef(forwardRef); };複製代碼
這裏有一個名爲 Input
的高階組件 ,它接受 InputComponent
做爲參數。當用戶輸入的時候,他還會將 ref
的值在控制檯打印。
在 Input
高階組件內,forwardRef
函數會返回 InputComponent
。forwardRef
函數中所包含的 ref
參數,是由 React.forwardRef
函數建立的。 高階組件最終會將包裝好的組件做爲值返回。
接下來,咱們建立一個組件,將 input
做爲子組件包含進來。
const TextInput = ({ forwardedRef, children, ...rest }) => (
<div> <input ref={forwardedRef} {...rest} /> {children} </div> );複製代碼
上面的組件會將 forwardedRef
分配給 ref
屬性, 當渲染子組件的時候,input
輸入框就會接收到這個 ref
。...rest
是 props
的解構(也就是說,咱們會將 rest
數組中的全部參數做爲 props
傳遞給 input
組件)。那麼咱們該如何使用 TextInput
組件呢?像這樣:
const InputField = Input(TextInput);
class CustomTextInput extends Component {
render() {
const inputRef = React.createRef();
return <InputField ref={inputRef} />;
}
}複製代碼
最後,將 TextInput
傳入 Input
高階組件,會返回一個 InputField component
。
建立一個 ref
,並做爲參數傳遞給 InputField
組件。
與經過 props
和 state
不一樣,Refs
是一種將數據傳遞給特定子實例的好方法。
你必需要當心,由於 refs
操縱實際的 DOM
,而不是虛擬的 DOM
,這與 React
思惟方式相矛盾。所以,雖然 refs
不該該是經過應用程序流動數據的默認方法,可是當您須要時,它們是能夠從 DOM
元素讀取數據的好方法。
譯註:推薦下「司徒正美」大佬的 React v16.3.0: New lifecycles and context API,createRef API,forwardRef API 中的示例能夠做爲補充閱讀。 本文已經聯繫原文做者,並受權翻譯,轉載請保留原文連接