关于ref
组件内使用ref,获取dom元素
class Test extends Component {
constructor(props) {
super(props);
this.state = {
myDiv:createRef()
}
}
componentDidMount(){
console.log("this.myDiv.current", this.state.myDiv.current);
// 打印结果: this.myDiv.current <div>ref获取的dom元素</div>
}
render() {
return (
<div ref={this.state.myDiv}>ref获取的dom元素</div>
);
}
}
export default Test;
ref作为子组件的属性,获取的是该子组件
import React, { Component, createRef } from 'react';
class Test extends Component {
constructor(props) {
super(props);
this.state = {
myDiv:createRef()
}
}
componentDidMount(){
console.log("this.myDiv.current", this.state.myDiv.current);
// 打印的是Child这个子组件(包括属性:props等),而不是Child里面的div
}
render() {
return (
<Child ref={this.state.myDiv}/>
);
}
}
class Child extends Component {
constructor(props) {
super(props);
this.state = { }
}
render() {
return (
<div>我是子组件</div>
);
}
}
export default Test;
如果想获得Child里面的div,应该如下修改:
// 父组件
<Child myRef={this.state.myDiv}/>
// 子组件
<div ref={this.props.myRef}>我是子组件</div>
关于forwardRef
简单使用forwardRef
const Child = forwardRef((props, ref)=>{
return (
<div ref={ref}>{props.txt}</div>
)
})
// 修改父组件
<Child ref={this.state.myDiv} txt="parent props txt"/>
ref
属性不能用在函数组件上,因为函数组件没有实例。
forwardRef在高阶组件中的使用
引用传递(Ref forwading)是一种通过组件向子组件自动传递 引用ref 的技术。对于应用者的大多数组件来说没什么作用。但是对于有些重复使用的组件,可能有用。例如某些input组件,需要控制其focus,本来是可以使用ref来控制,但是因为该input已被包裹在组件中,这时就需要使用Ref forward来透过组件获得该input的引用。
作者:pipu
链接:https://www.jianshu.com/p/fac884647720
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
之前在 @Dell Lee 的一篇文章中看到说没有办法传递ref到一个functional stateless component,当时十分naive的项目中实验了一下,以为能够传递ref,结果发现是因为我的疏忽,这个组件上面还有一层HOC,而这个HOC不是functional stateless component我误以为能够传递ref。不过也在探讨过程中发现只要名字不是ref的话其实就可以通过props进行传递。
不过这一切都将成为历史,伟大的fb在react 16.3中新加了一个React.forwardRef函数使这一切变得非常符合我的口味!
import React, { Component, createRef, forwardRef } from 'react';
const bindRef = (WrapperComponent)=>{
const ConvertRef = (props)=>{
const { forwardRef, ...other } = props;
return <WrapperComponent {...other} ref={forwardRef}/>
}
return forwardRef((props, ref)=>{
return (
<ConvertRef {...props} forwardRef={ref} />
)
})
}
const Child = forwardRef((props, ref)=>{
return (
<div ref={ref}>{props.txt}</div>
)
})
const Wrappr = bindRef(Child);
class Test extends Component {
constructor(props) {
super(props);
this.myDiv = createRef();
}
componentDidMount(){
console.log("this.myDiv.current", this.myDiv.current);
}
render() {
return (
<Wrappr ref={this.myDiv} txt="parent props txt"/>
);
}
}
export default Test;
关于useRef
比较createRef
和useRef
在第一个例子中,我们使用了createRef
,为了对比useRef
(因为useRef
只能在函数组件中使用),所以我们接下来我们把它改成函数组件:
import React, {createRef, useEffect } from 'react';
const Test = ()=>{
const myDiv = createRef();
useEffect(()=>{
console.log("this.myDiv.current", myDiv.current);
})
return(
<div ref={myDiv}>ref获取的dom元素</div>
)
}
export default Test;
我们用useRef
替换createRef
:
const myDiv = useRef();
效果是一样的。所以为什么会用useRef
?
createRef 与 useRef 的区别
useRef
returns a mutable ref object whose.current
property is initialized to the passed argument (initialValue
). The returned object will persist for the full lifetime of the component.换句人话说 , useRef 在 react hook 中的作用, 正如官网说的, 它像一个变量, 类似于 this , 它就像一个盒子, 你可以存放任何东西.
*createRef 每次渲染都会返回一个新的引用,而 useRef 每次都会返回相同的引用。*
import React, {useState, useRef, createRef, useEffect } from 'react';
const Test = ()=>{
const [ index, setIndex ] = useState(1);
const createRefDiv = createRef();
const useRefDiv = useRef();
if(!createRefDiv.current){
createRefDiv.current = index;
}
if(!useRefDiv.current){
useRefDiv.current = index;
}
return(
<>
<div>createRefDiv.current: {createRefDiv.current}</div>
<div>useRefDiv.current: {useRefDiv.current}</div>
<div>
<button onClick={()=>setIndex(index+1)}>改变Index</button>
</div>
</>
)
}
export default Test;
初始化时createRefDiv.current
和useRefDiv.current
都等于index
,当点击按钮的时候,createRefDiv.current
的值发生了改变,但是useRefDiv.current
没有。
实际场景
import React, {useState, useRef, createRef, useEffect } from 'react';
const Test = ()=>{
const [ index, setIndex ] = useState(1);
const handleAlertClick = ()=>{
setTimeout(()=>{
alert("You clicked on: " + index );
},3000);
}
return(
<>
<div>You clicked { index } times.</div>
<button onClick={()=>setIndex(index+1)}>改变Index</button>
<button onClick={handleAlertClick}>弹出Index</button>
</>
)
}
export default Test;
当点击改变index
的按钮一次之后,点击弹出Index
,之后再多次点击改变index
,最后弹出来的index
的值时2。
import React, {useState, useRef, createRef, useEffect } from 'react';
const Test = ()=>{
const [ index, setIndex ] = useState(1);
const lastestIndex = useRef();
useEffect(()=>{
lastestIndex.current = index;
})
const handleAlertClick = ()=>{
setTimeout(()=>{
alert("You clicked on: " + lastestIndex.current );
},3000);
}
return(
<>
<div>You clicked { index } times.</div>
<button onClick={()=>setIndex(index+1)}>改变Index</button>
<button onClick={handleAlertClick}>改变Index</button>
</>
)
}
export default Test;
因为 useRef 每次都会返回同一个引用, 所以在 useEffect 中修改的时候 ,在 alert 中也会同时被修改. 这样子, 点击的时候就可以弹出实时的 index了
获取上一次的值
import React, {useState, useRef, createRef, useEffect } from 'react';
const Test = ()=>{
const [ index, setIndex ] = useState(1);
const lastestIndex = useRef();
useEffect(()=>{
lastestIndex.current = index;
})
return(
<>
<div>You clicked { index } times.</div>
<div>lastestIndex.current: { lastestIndex.current }.</div>
<button onClick={()=>setIndex(index+1)}>改变Index</button>
</>
)
}
export default Test;
改变为自定义hook
import React, {useState, useRef, createRef, useEffect } from 'react';
const useLastestIndex = state =>{
const ref = useRef();
useEffect(()=>{
ref.current = state;
})
return ref.current;
}
const Test = ()=>{
const [ index, setIndex ] = useState(1);
const lastestIndex = useLastestIndex(index);
return(
<>
<div>You clicked { index } times.</div>
<div>lastestIndex: { lastestIndex }.</div>
<button onClick={()=>setIndex(index+1)}>改变Index</button>
</>
)
}
export default Test;
总结
useRef
就像是可以在其.current
属性中保存一个可变值的“盒子”;ref
对象以<div ref={myRef} />
形式传入组件,则无论该节点如何改变,React 都会将 ref 对象的 .current 属性设置为相应的 DOM 节点,但是useRef()比
ref` 属性更有用。它可以很方便地保存任何可变值;useRef
会在每次渲染时返回同一个 ref 对象;- 当 ref 对象内容发生变化时,
useRef
并不会通知你。变更.current
属性不会引发组件重新渲染。
参考: