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();

浙公网安备 33010602011771号