React中的refs提供了一种方式,允许我们访问dom节点或者是在render方法中创建的react元素。
它的本质是ReactDOM.render返回的组件实例,如果是渲染组件返回的就是组件实例,如果是渲染DOM,然回的就是dom节点。
创建ref的形式有三种:
1.传入字符串,使用的时候通过 this.refs.传入的字符串 的格式来获取对应的元素,如下:
<!DOCTYPE html> <html> <head> <title>字符串形式的ref</title> </head> <body> <!-- 1.创建一个容器 --> <div id="test"></div> <!-- 2.导入核心包,必须按照顺序来导入 --> <script type="text/javascript" src="../js/react.development.js"></script> <script type="text/javascript" src="../js/react-dom.development.js" ></script> <script type="text/javascript" src="../js/babel.min.js"></script> <div id="test"></div> <!-- 3.编写代码:注意使用的type是text/babel --> <!-- React里面的是使用refs,Vue里面是使用$refs,refs拿的是真实的dom节点,而不是虚拟的dom --> <!-- 字符串形式的ref并不推荐使用,因为效率问题,可能在后面的版本中弃用 --> <script type="text/babel"> class Dome extends React.Component { // 注意如果要将方法里面的this绑定到组件实例上,需要的格式是使用=号加上箭头函数 showData1 = () => { alert(this.refs.input1.value) } showData2 = () => { alert(this.refs.input2.value) } render() { return ( <div> <input type='text' ref='input1' /> {/*jsx里面的事件和原生的有些不同,原生的是onclick ,jsx里面的是onClick*/} <button ref='button1' onClick={this.showData1}> 点击我显示输入框的数据 </button> <input type='text' ref='input2' placeholder='失去焦点显示数据' onBlur={this.showData2} /> </div> ) } } ReactDOM.render(<Dome />, document.getElementById('test')) </script> </body> </html>
2.传入对象,对象是通过 React.createRef() 方法创建出来的,一个对象只能包含一个dom,使用时候获取到创建的对象中存在current属性就是对应的元素,如下:
<!DOCTYPE html> <html> <head> <title>字符串形式的ref</title> </head> <body> <!-- 1.创建一个容器 --> <div id="test"></div> <!-- 2.导入核心包,必须按照顺序来导入 --> <script type="text/javascript" src="../js/react.development.js"></script> <script type="text/javascript" src="../js/react-dom.development.js" ></script> <script type="text/javascript" src="../js/babel.min.js"></script> <div id="test"></div> <script type="text/babel"> class Dome extends React.Component { myRef1 = React.createRef() // 创建一个存放dom的ref盒子,但是一个只能存放一个dom,如果多次使用同一个,后面的会覆盖前面的 myRef2 = React.createRef() showData1 = () => { let input = this.myRef1.current alert(input.value) } showData2 = () => { let input = this.myRef2.current alert(input.value) } render() { return ( <div> <input type='text' ref={this.myRef1} /> <button onClick={this.showData1}>点击我显示输入框的数据</button> <input type='text' ref={this.myRef2} placeholder='失去焦点显示数据' onBlur={this.showData2} /> </div> ) } } ReactDOM.render(<Dome />, document.getElementById('test')) </script> </body> </html>
3.传入回调函数,该函数会在DOM挂载的时候进行回调,这个函数会传入一个元素对象,可以自己保存,使用的时候,直接拿到之前保存的元素对象就可以了
<!DOCTYPE html> <html> <head> <title>字符串形式的ref</title> </head> <body> <!-- 1.创建一个容器 --> <div id="test"></div> <!-- 2.导入核心包,必须按照顺序来导入 --> <script type="text/javascript" src="../js/react.development.js"></script> <script type="text/javascript" src="../js/react-dom.development.js" ></script> <script type="text/javascript" src="../js/babel.min.js"></script> <div id="test"></div> <!-- 3.编写代码:注意使用的type是text/babel --> <!-- React里面的是使用refs,Vue里面是使用$refs,refs拿的是真实的dom节点,而不是虚拟的dom --> <script type="text/babel"> class Dome extends React.Component { // 注意如果要将方法里面的this绑定到组件实例上,需要的格式是使用=号加上箭头函数 showData1 = () => { let { input1 } = this // 注意:这个时候是从当前组件实例上拿去dom节点,而而不是从refs上 alert(input1.value) } showData2 = () => { let { input2 } = this // 注意:这个时候是从当前组件实例上拿去dom节点,而而不是从refs上 alert(input2.value) } render() { return ( <div> {/* 回调形式的ref c是当前的dom节点,将当前节点赋值给组件实例的input1上,注意这里的this的是组件实例 */} <input type='text' ref={(c) => (this.input1 = c)} /> {/*jsx里面的事件和原生的有些不同,原生的是onclick ,jsx里面的是onClick*/} <button onClick={this.showData1}>点击我显示输入框的数据</button> <input type='text' ref={(c) => (this.input2 = c)} placeholder='失去焦点显示数据' onBlur={this.showData2} /> </div> ) } } ReactDOM.render(<Dome />, document.getElementById('test')) </script> </body> </html>
如果 ref
回调函数是以内联函数的方式定义的,在更新过程中它会被执行两次,第一次传入参数 null
,然后第二次会传入参数 DOM 元素。这是因为在每次渲染时会创建一个新的函数实例,所以 React 清空旧的 ref 并且设置新的。通过将 ref 的回调函数定义成 class 的绑定函数的方式可以避免上述问题,但是大多数情况下它是无关紧要的。
4传入hook,hook是通过useRef()方式来创建的,使用时候通过生成hook对象的current属性就是对应的元素。(Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。React的三大特性ref,state,props其中的ref和state是类式组件特有的,而函数式组件式没有的)
function App(props) { const ref = useRef() return ( <div> <div ref={myRef}>1111</div> </div> ) }
获取ref属性也式通过hook对象的current属性来获取。
const node = myref.current;
需要注意的是:不能在函数式组件上使用ref属性,因为他们并没有实例。
过多的使用refs,会使组件的实例或者式DOM结构暴露,违反了封装组件的原则。
例如,在Dialog组件里面暴露了open和close方法,通常我们可以传递一个isOpen属性来控制弹窗的显示
下面的场景可以使用refs:
(1)对dom元素的焦点控制,内容选择,控制。
(2)对dom元素的内容设置和媒体播放
(3)对dom元素的操作和对组件的实例的操作
(4) 集成第三方的DOM库