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

  

posted @ 2022-12-07 22:37  飞奔的龟龟  阅读(11)  评论(0)    收藏  举报