refs&dom样例详解
import * as React from 'react' import * as ReactDOM from 'react-dom/client'; import {createPortal} from 'react-dom' const root = ReactDOM.createRoot(document.getElementById('root')) // const CustomInput = React.forwardRef((props,ref)=>{ // return ( // <div> // <input /> // </div> // ) // }) //你不能在函数组件上使用ref属性 因为他们没有实例 //但是你可以通过转发的形式 来给函数组件添加ref属性 class CustomInput extends React.Component{ constructor(props){ super(props); } open(){ alert(123) } render(){ return ( <div> <input /> </div> ) } } // const CustomInput = (props)=>{ // return ( // <div> // <input /> // </div> // ) // } //在特殊情况下你可能需要将子组件的dom暴漏给父组件 //16.3以上的版本我们可以通过转发 //16.3以下的版本 我们可以使用将refs作为一个特殊的props向下传递 或者使用回调refs //不同于传递createref创建的ref属性 //refs回调函数 接受react组件实例或者是dom元素作为参数,使它能够在其他地方被存储和访问 //如果refs回调函数是以内联函数定义的,那么他会被执行二次一次是传null 第二次才是传递真正的dom元素 //因为组件重新渲染会生成一个新的函数实例,react会清空旧的ref并设置新的ref //我们可以通过像绑定class函数那样的形式 去设置refs回调 就不会出现上述问题了 class App extends React.Component{ constructor(props){ super(props); this.inputRef = null; //此回调接受一个组件实例或者是dom元素作为参数 this.refsCallback = (el)=>{ console.log(el) this.inputRef = el; } this.handleClick = this.handleClick.bind(this); } handleClick(){ this.inputRef.open() } componentDidMount(){ console.log(this.inputRef) } render(){ return ( <div> <CustomInput ref = {this.refsCallback } /> <button onClick={this.handleClick}>点击我文本框获取焦点</button> </div> ) } } //不管怎样你可以在函数内部使用ref属性 指定一个dom或者是class组件 // function App(){ // const textInput = React.useRef(null); // function handleClick(){ // textInput.current.focus(); // } // return ( // <div> // <CustomInput forwardRef={textInput} /> // <button onClick={handleClick}>点击我文本框获取焦点</button> // </div> // ) // } root.render( <App /> );
refs总结:
1、refs作用于html元素,构造组件时 创建的ref对象 会接收底层的dom节点作为其current属性
2、refs作用于组件,构造组件时 创建的ref对象 会接收组件挂载的实例作为其current属性
3、不能为函数式组件指定ref属性,因为函数式组件没有实例(可以通过转发ref的方式 转发给函数式组件)
创建refs的三种方式:
1、字符串ref(已弃用)
2、createRef api创建ref
3、回调式ref
import React, { Children, useState, useTransition,Profiler,useRef,forwardRef,useImperativeHandle} from 'react'; import ReactDOM from 'react-dom/client'; import { createPortal } from 'react-dom'; import './index.css'; import App from './App'; import reportWebVitals from './reportWebVitals'; //拷贝所有非react的静态方法 import hoistNonReactStatic from 'hoist-non-react-statics'; const root = ReactDOM.createRoot(document.getElementById('root')); //ref属性作用于html元素时 构造函数中创建的ref 会接收底层的dom元素作为其current属性 //ref属性作用于组件时 构造函数中创建的ref 会接收挂载的组件实例作为其current属性 //函数组件不能接收ref属性 因为函数组件没有实例 //将ref附加到react元素上 //如果对子组件的选择没有控制权的话,你可以直接使用findDomNode 但是已经被禁用了 避免使用他 class CustomTextInput extends React.Component{ constructor(props){ super(props); this.inputRef = React.createRef(); this.handleFoucs = this.handleFoucs.bind(this); } handleFoucs(){ console.log('走这里了') this.inputRef.current.focus(); } render(){ return ( <form> <label> 选中框1: <input ref={this.inputRef} /> </label> </form> ) } } function CustomTextInput2(props,ref){ //函数式组件内部使用ref const domRef = useRef(); useImperativeHandle( ref, () => { return { getDom:()=>{ console.log(domRef.current) } } }, [] ) return ( <div ref={domRef}>测试</div> ) } CustomTextInput2 = forwardRef(CustomTextInput2) class CustomTextInput3 extends React.Component{ constructor(props){ super(props) } render(){ return null; } } //componentDidMount componentDidUpdate 在这二个生命周期触发之前 react会保证refs一定是最新的 class AutoFocusTextInput extends React.Component{ constructor(props){ super(props); this.inputExample = React.createRef(); //第一步:创建ref 并将ref分配给组件的实例属性 这样就可以在全局引用他了 //第二步:通过ref属性 将创建ref附加到react元素上 (附加的元素有可能是htmldom节点或者是react组件) //第三步:组件在挂载时 会将对应的react元素传入到 创建的ref的current属性上 会在componentDidMount componentDidUpdate生命周期触发之前更新 this.tempRef = React.createRef(); //ref回调 //通过实例属性 来存储组件的实例或者是dom元素 this.aa = null; this.callFn = elem=>{ this.aa = elem; } } componentDidMount(){ this.inputExample.current.handleFoucs(); console.log(this.aa) } render(){ return ( // 这个组件必须是class组件 不能在函数组件中使用ref属性 因为函数式组件没有实例 //不管怎样 你可以在函数组件的内部使用ref属性 <> <CustomTextInput ref={this.inputExample} /> <CustomTextInput2 ref={this.tempRef} /> <CustomTextInput3 ref={this.callFn} /> </> ) } } function P2(props){ return ( <div ref={props.inputRef}> 王武2 </div> ) } class P1 extends React.Component{ constructor(props){ super(props); this.el = null; //使用class 绑定函数的方式 解决此问题 this.inputFn = (el)=>{ console.log('这里只走一次') this.el = el } } componentDidMount(){ console.log(this.el) } render(){ // <P2 inputRef={(el)=>{ // // 内联的ref回调 等到更新时会被调用二次第一次传null 第二次传入dom元素 // // 内联的每次更新时都会创建新的函数实例,会清空旧的ref并设置新的ref // console.log('内联的每次更新时都会创建新的函数实例,会清空旧的ref并设置新的ref') // console.log(el) // this.iref = el // }} /> return ( <P2 inputRef={this.inputFn} /> ) } } class Apps extends React.Component{ constructor(props){ super(props) //创建refs //并通过ref属性 附加到react元素当中 this.input = React.createRef(); this.state = { count:1 }; //为什么绑定this //原因1:react组件的class与普通的class的编写一样 里面的方法不会自动绑定this到实例上 这与js的运行时环境有关系 //原因2:当class中的方法 被赋值给一个变量使用或者是当做回调函数使用时 方法里面的this也不会自动绑定到实例上 所以需要手动绑定 //绑定的方法:bind 或者箭头函数 因为箭头函数有自己的词法环境 this会被静态的绑定 this.handleClick = this.handleClick.bind(this); } handleClick(){ this.setState((state)=>({ count:state.count + 1 })) } componentDidMount(){ //会在组件挂载时给current属性,传入dom元素 并在卸载时给current属性传入null //ref会在 componentDidMount 和 componentDidUpdate 生命周期钩子触发之前更新 // this.input.current.focus() // console.log(this.input) } render(){ return ( <> <button onClick={this.handleClick}>{this.state.count}</button> <AutoFocusTextInput /> <P1 /> </> ) } } root.render(<Apps />) //父组件访问子组件中dom元素的节点 有一下方法 //1、refs转发 //2、通过一个特殊的props传递下去 //3、回调ref // If you want to start measuring performance in your app, pass a function // to log results (for example: reportWebVitals(console.log)) // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals reportWebVitals();