Skip to content

Commit 7acb324

Browse files
committed
添加react的ref说明
1 parent de39dc4 commit 7acb324

File tree

1 file changed

+221
-0
lines changed

1 file changed

+221
-0
lines changed

react-ref/index.md

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
### 1.为DOM元素添加Ref
2+
react支持一个ref属性,该属性可以添加到任何的组件上。该ref属性接收一个回调函数,这个回调函数在组件挂载或者卸载的时候被调用。当ref用于一个HTML元素的时候,ref指定的回调函数在调用的时候会接收一个参数,该参数就是指定的DOM元素。如下面的例子使用ref回调函数来保存对DOM节点的引用:
3+
```js
4+
class CustomTextInput extends React.Component {
5+
constructor(props) {
6+
super(props);
7+
this.focus = this.focus.bind(this);
8+
}
9+
focus() {
10+
// Explicitly focus the text input using the raw DOM API
11+
this.textInput.focus();
12+
}
13+
render() {
14+
// Use the `ref` callback to store a reference to the text input DOM
15+
// element in an instance field (for example, this.textInput).
16+
return (
17+
<div>
18+
<input
19+
type="text"
20+
ref={(input) => { this.textInput = input; }} />
21+
//此时input参数就是表示该DOM本身
22+
<input
23+
type="button"
24+
value="Focus the text input"
25+
onClick={this.focus}
26+
/>
27+
</div>
28+
);
29+
}
30+
}
31+
```
32+
当组件挂载的时候React会给ref回调函数传入当前的DOM,在组件卸载的时候会传入null。使用ref回调函数给我们的class添加属性是访问组件DOM的常用方法,就像上面的实例一样。当然你也可以使用下面更加简短的写法:
33+
```js
34+
ref={input=>this.textInput=input}
35+
```
36+
37+
### 2.为组件Component添加Ref
38+
当ref属性用于一个class指定的自定义组件的时候,ref回调函数会接收到一个挂载的组件实例作为参数。比如,下面的例子展示了当CustomTextInput被挂载后马上模拟被点击:
39+
```js
40+
class AutoFocusTextInput extends React.Component {
41+
componentDidMount() {
42+
//调用CustomTextInput实例的focus方法
43+
this.textInput.focus();
44+
}
45+
render() {
46+
return (
47+
<CustomTextInput
48+
ref={(input) => { this.textInput = input; }} />
49+
//该ref回调函数会接收到一个挂载的组件实例作为参数
50+
);
51+
}
52+
}
53+
```
54+
当然,下面的CustomTextInput必须使用class来声明:
55+
```js
56+
class CustomTextInput extends React.Component {
57+
constructor(props) {
58+
super(props);
59+
this.focus = this.focus.bind(this);
60+
}
61+
focus() {
62+
// Explicitly focus the text input using the raw DOM API
63+
this.textInput.focus();
64+
}
65+
render() {
66+
// Use the `ref` callback to store a reference to the text input DOM
67+
// element in an instance field (for example, this.textInput).
68+
return (
69+
<div>
70+
<input
71+
type="text"
72+
ref={(input) => { this.textInput = input; }} />
73+
<input
74+
type="button"
75+
value="Focus the text input"
76+
onClick={this.focus}
77+
/>
78+
</div>
79+
);
80+
}
81+
}
82+
```
83+
注意,上面的ref回调函数接收到的是一个CustomTextInput的实例,在componentDidMount中调用的是CustomTextInput实例的focus方法,但是该focus方法必须在CustomTextInput这个class中存在才行。下面的声明将会报错,因为CustomTextInput本身就没有focus方法:
84+
```js
85+
class CustomTextInput extends React.Component {
86+
render(){
87+
return (
88+
<input placeholder="自定义组件实例"/>
89+
)
90+
}
91+
}
92+
```
93+
报错信息如下:
94+
95+
<pre>
96+
Uncaught TypeError: this.textInput.focus is not a function
97+
at AutoFocusTextInput.componentDidMount (<anonymous>:24:22)
98+
</pre>
99+
100+
### 3.Ref与函数式声明组件
101+
你不能在函数式声明组件中使用ref,因为他们*不存在实例*,如下面的例子就是错误的:
102+
```js
103+
function MyFunctionalComponent() {
104+
return <input />;
105+
}
106+
class Parent extends React.Component {
107+
render() {
108+
// This will *not* work!
109+
return (
110+
<MyFunctionalComponent
111+
ref={(input) => { this.textInput = input; }} />
112+
);
113+
}
114+
}
115+
```
116+
如果你想要使用ref那么你必须转化为class声明。当然,在函数组件内部你依然可以使用ref属性指向DOM元素或者class组件:
117+
```js
118+
function CustomTextInput(props) {
119+
// textInput must be declared here so the ref callback can refer to it
120+
let textInput = null;
121+
function handleClick() {
122+
textInput.focus();
123+
}
124+
return (
125+
<div>
126+
<input
127+
type="text"
128+
ref={(input) => { textInput = input; }} />
129+
<input
130+
type="button"
131+
value="Focus the text input"
132+
onClick={handleClick}
133+
/>
134+
</div>
135+
);
136+
}
137+
```
138+
139+
### 4.为父组件暴露一个DOM的ref属性
140+
在一些情况下,你可能想要从父级组件中访问子级组件的DOM节点。当然这是不推荐的,因为它破坏了组件的结构。但是,它在触发子级组件DOM的焦点,获取子级组件大小和子级组件位置的时候非常有用。
141+
142+
你可以为子级组件添加一个ref属性,当然这不是理想选择,因为此时你获取到的是一个子级组件实例而不是一个DOM节点。而且,这对于函数式组件是没有作用的(见上面的例子)。
143+
144+
在这种情况下,我们推荐在子级组件中暴露一个特殊的属性。这个子级组件会接收一个函数作为prop属性,而且该函数的名称是任意的(例如inputRef),同时将这个函数赋予到DOM节点作为ref属性。这样,父级组件会将它的ref回调传递给子级组件的DOM。这种方式对于class声明的组件和函数式声明的组件都是适用的:
145+
```js
146+
function CustomTextInput(props) {
147+
return (
148+
<div>
149+
//子级组件接收到父级组件传递过来的inputRef函数
150+
//当子级组件实例化的时候会将该input传入到该函数中,此时父级组件会
151+
//接收到子级组件的DOM结构(input元素)
152+
<input ref={props.inputRef} />
153+
</div>
154+
);
155+
}
156+
class Parent extends React.Component {
157+
render() {
158+
return (
159+
<CustomTextInput
160+
//为子级组件传入一个inputRef函数
161+
inputRef={el => this.inputElement = el}
162+
\/>
163+
);
164+
}
165+
}
166+
```
167+
在上面的例子中,Parent将他的ref回调函数通过inputRef这个属性传递给CustomTextInput,而CustomTextInput将这个函数作为input元素的ref属性。最后,Parent组件中的this.inputElement将得到子级组件的input对应的DOM元素。注意:inputRef并没有特殊的含义,但是在子级组件中必须作为ref属性的值。
168+
169+
这种形式的又一个优点在于:可以跨越多个组件层级。设想一种情况:Parent组件不需要DOM节点的引用,但是父级组件的父级组件(Grandparent)需要,这样的话我们可以给Grandparent指定一个inputRef属性,从而传递给Parent,最后通过Parent组件传递给CustomTextInput:
170+
```js
171+
return (
172+
<div>
173+
<input ref={props.inputRef} />
174+
</div>
175+
);
176+
}
177+
function Parent(props) {
178+
return (
179+
<div>
180+
My input: <CustomTextInput inputRef={props.inputRef} />
181+
</div>
182+
);
183+
}
184+
class Grandparent extends React.Component {
185+
render() {
186+
return (
187+
<Parent
188+
inputRef={el => this.inputElement = el}
189+
\/>
190+
);
191+
}
192+
}
193+
```
194+
这样的话,我们的GrandParent中的this.inputElement也会被设置为CustomTextInput中的input对应的DOM节点。当然,这种方式必须要你对子级组件具有完全控制,如果没有,那么你可以使用findDOMNode(),但是我们并不鼓励这么做。
195+
196+
### 5.ref遗留的问题
197+
以前的ref属性获取到的是字符串,而DOM节点通过this.refs.textInput来获取。这是因为string类型的ref有一定的问题,在以后的react版本中将会被移除。如果你现在依然在使用this.refs.textInput来访问refs,那么建议您使用回调函数来替代!
198+
199+
### 6.ref存在的问题以及ref常用情况
200+
#### 6.1 Ref存在的问题
201+
如果ref回调函数以inline函数的方式来指定,那么在组件更新的时候ref回调会被调用2次。第一次回调的时候传入的参数是null,而第二次的时候才真正的传入DOM节点。这是因为,每次渲染的时候都会产生一个新的函数实例(每次都会产生一个新的函数,而不是单例模式或者设置到原型链中的函数),而React需要清除前一个ref,然后才设置一个新的ref。通过在class中指定ref回调函数可以有效的避免这种情况。但是,在大多数情况下这并不会有什么影响。
202+
203+
#### 6.2 Ref常用情况
204+
205+
第一:管理焦点,文本选择,媒体播放(媒体回放)
206+
207+
第二:触发动画
208+
209+
第三:集成第三方的DOM库
210+
211+
212+
213+
214+
215+
216+
217+
参考资料:
218+
219+
[Refs and the DOM](https://facebook.github.io/react/docs/refs-and-the-dom.html)
220+
221+
[Integrating with Other Libraries](https://facebook.github.io/react/docs/integrating-with-other-libraries.html)

0 commit comments

Comments
 (0)