六、React学习笔记整理(refs)

253 阅读3分钟

什么是ref

组件内的标签可以定义ref属性来标识⾃⼰,然后通过ref标识来获取dom元素,进而操作dom元素(react中不提倡过多操作dom)。

ref的三种创建方式

1.字符串形式的ref

// 1. 创建类式组件
class Demo extends React.Component {
    render() {
        return (
            <div>
                {/* 字符串形式的ref */}
                <input ref="input1" type="text" placeholder="请输入文字"/>&nbsp;
                <button onClick={this.showData}>点击显示左侧数据</button>&nbsp;
            </div>
        )
    }
    // 显示左侧输入的数据
    showData = () => {
        let {input1} = this.refs
        alert(input1.value)
    }
}
// 2. 渲染组件到页面
ReactDOM.render(<Demo />, document.getElementById('test'))

2.回调形式的ref

字符串形式的ref由于存在效率上的问题, react官⽅不在推荐使⽤, 甚⾄可能在后续版本移除。
react官⽅推荐使⽤回调形式的的ref或者createRef

// 1. 创建类式组件
class Demo extends React.Component {
    render() {
        return (
            <div>
                {/* 回调形式的ref */}
                <input ref={c => this.input1 = c} type="text" placeholder="请输入文字"/>
                <button onClick={this.showData}>点击显示左侧数据</button>&nbsp;
            </div>
        )
    }
    // 显示左侧输入的数据
    showData = () => {
        let {input1} = this.refs
        alert(input1.value)
    }
}
// 2. 渲染组件到页面
ReactDOM.render(<Demo />, document.getElementById('test'))

什么是回调函数?

  1. 你定义的函数
  2. 你没有调⽤
  3. 函数最终执⾏了(别⼈调了)

3.createRef的使⽤

React.createRef调⽤之后返回⼀个容器, 该容器可以存储被ref标识的节点 该容器是专⼈专⽤, 每⼀个input都要使⽤单独的⼀个 虽然写法⽐较繁琐, 却是React官⽅最推荐的写法

class Demo extends React.Component{
  // React.createRef 是一个容器,这个容器可以存储一个被ref标记的dom节点。
  // 每一个由React.createRef创建的容器仅被使用一次,不允许重复使用。
  input1 = React.createRef()

  showData = () => {
    const {current} = this.input1
    console.log(this);
    alert(current.value)
  }

  inputBlur = () => {
      const {current} = this.input1
      console.log(this);
      alert(current.value)
  }
  render() {
    return (
      <div>
          <input ref={this.input1} type="text" placeholder="点击按钮显示数据"/>&nbsp;
          <button onClick={this.showData}>点击显示左侧数据</button>&nbsp;
           {/**此时在这里让ref使用this.input1,是不对的,因为已经在上个input中使用过了**/}
          <input ref={this.input1} type="text" onBlur={this.inputBlur} placeholder="焦点离开显示数据"/>
      </div>
    );
  }
}
ReactDOM.render(<Demo/>, document.getElementById('test'))

如果出现重复使用的ref,那么此ref标记会标记最后一个使用该ref的dom元素,之前标记的dom元素获取不到。

回调ref中调⽤次数的问题

class Demo extends React.Component{
  state = {isHot: false}
  showData = () => {
    const {input1} = this
    alert(input1.value)
  }
  changeWeather = () => {
    this.setState({isHot: !this.state.isHot})
  }
  refFunction = (param) => {
    console.log('bb', param);
  }
  render() {
    const {isHot} = this.state
    return (
      <div>
          <h1 onClick={this.changeWeather}>{isHot ? '炎热': '凉爽'}</h1>
          {/**内联形式的ref回调, ref回调函数会执行两次*/}
          <input ref={(c) => {this.input1 = c; console.log('aa', c);}} type="text" placeholder="点击按钮显示数据"/>&nbsp;
         {/** 写成类的绑定函数,可以避免ref执行两次*/}
          <input ref={this.refFunction} type="text" placeholder="点击按钮显示数据"/>&nbsp;
          <button onClick={this.showData}>点击显示左侧数据</button>&nbsp;
      </div>
    );
  }
}

一、如果 ref 回调函数是以"内联函数"的方式定义的,在更新过程中它会被执行两次,第一次传入参数 null,然后第二次会传入参数 DOM 元素。
       这是因为在每次渲染时会创建一个新的函数实例。所以 React 清空旧的 ref 并且设置新的。这个时候会调用一次,此时传入的参数式null。之后等渲染的时候会再调用一次,
二、 通过将 ref 的回调函数定义成 class 的绑定函数的方式可以避免上述问题。 通过将方法绑定到类上这种方式,由于直接挂载到实例上了,就不需要重新生成函数,也就不需要重新执行了。

总结

  1. 尽量避免使⽤字符串形式的ref, 如果⾮要⽤, 也没什么问题
  2. 回调形式的ref有内联函数回调形式, 还有class函数回调形式, 没什么⼤影响,⽤内联即可。
  3. createRef⽐较复杂, 但是官⽅最为推荐。