使用 this.refs 的弃用警告

IT技术 javascript reactjs
2021-03-24 02:22:30

我有一个 React 组件,我想在单击时切换一个 css 类。

所以我有这个:

export class myComponent extends React.Component {
  constructor() {
    super();
    this.state = { clicked: false };
    this.handleClick = this.handleClick.bind(this);
  }

  render() {
    return (
      <div>
        <div onClick={this.clicked}><span ref="btn" className="glyphicon">&nbsp;</span></div>
      </div>
    );
  }

  handleClick() {
    this.refs.btn.classList.toggle('active');
  }

  componentDidMount() {
    this.refs.btn.addEventListener('click', this.handleClick);
    this.setState({
      clicked: this.state.clicked = true,
    });
  }

  componentWillUnmount() {
    this.refs.btn.removeEventListener('click', this.handleClick);
    this.setState({
      clicked: this.state.clicked = false,
    });
  }
}

这个问题是 ESLint 一直告诉我“this.refs”已贬值。

我该怎么办?我该如何修复它以使其不使用折旧的代码?

3个回答

您所指的 Lint 规则称为no-string-refs并警告您:

"Using string literals in ref attributes is deprecated (react/no-string-refs)"

您收到此警告是因为已经实现了已弃用的使用方式refs(通过使用字符串)。根据您的 React 版本,您可以执行以下操作:

react 16.3 及更高版本

constructor() {
  super();
  this.btnRef= React.createRef();
  this.state = { clicked: false };
  this.handleClick = this.handleClick.bind(this);
}

render() {
  return (
    <div>
      <div onClick={this.addVote}><span ref={this.btnRef} className="glyphicon">&nbsp;</span></div>
    </div>
  );
}

React 16.2 及更早版本

constructor() {
  super();
  this.btnRef;  //not necessary to declare the variable here, but I like to make it more visible.
  this.state = { clicked: false };
  this.handleClick = this.handleClick.bind(this);
}

render() {
  return (
    <div>
      <div onClick={this.addVote}><span ref={(el) => this.btnRef = el} className="glyphicon">&nbsp;</span></div>
    </div>
  );
}

为了获得更好的可读性,您还可以执行以下操作:

render() {
  let myRef = (el) => this.btnRef = el;
  return (
    <div>
      <div onClick={this.addVote}><span ref={myRef} className="glyphicon">&nbsp;</span></div>
    </div>
  );
}

看看官方文档对Refs 和 DOM 的描述特别是本节

旧 API:字符串引用

如果您之前使用过 React,您可能熟悉旧 API,其中ref属性是一个字符串,例如 "textInput",而 DOM 节点作为 访问this.refs.textInput我们建议不要这样做,因为字符串引用有一些问题,被认为是遗留的,并且可能会在未来的版本之一中被删除如果您当前正在使用this.refs.textInput访问 refs,我们建议改用回调模式。

如何访问componentWillUnmount()生命周期钩子中的引用
2021-05-24 02:22:30
@essaji,就像您在应用程序中的任何地方一样。例如,在我上面的代码段中,您可以使用this.btnRef.
2021-06-06 02:22:30

这个 ESLint 规则存在的原因是字符串 Refs 即将消失。但是,对于上面的代码,我建议首先不要使用 Ref。

不要过度使用 Refs

React 的优点是它是声明性的。意思是,我们有状态和一个表达式(返回的 JSX),表示给定某种状态时 UI(更准确地说是 DOM)应该是什么样子。

无论仅使用状态和 UI 表达式可以完成什么,都应该以这种方式完成。在上面的代码中使用 Ref 的问题在于它使代码变得必要。我们无法仅从 JSX 中了解 DOM 的外观。以下是如何以声明方式实现相同的结果:

export class myComponent extends React.Component {
    constructor(props) {
        super(props);
        this.state = { 
            active: false 
        };
    }

    handleClick = () => {  // with arrow function there is no need for binding. 
        this.setState(
            prevState => {
                return {
                    active: !prevState.active
                }
            }
        )
    }

    render() {
        return (
            <div>
                <span 
                    onClick={this.handleClick} 
                    className={`glyphicon ${this.state.active && "active"}`}
                >
                    Hello World
                </span>
            </div>
        );
    }

}

当状态和 UI 表达式不够用,并且您需要访问实际 DOM 时,应该使用 Refs。例如,关注输入字段、滚动到元素或获取元素的确切宽度和高度。

如果您确实使用 Refs,请避免使用字符串 refs

字符串 refs 会损害性能,不可组合,并且有出路。

字符串引用有一些问题,被认为是遗留的,可能会在未来的版本之一中被删除。[官方 React 文档]

[资源 1][1]、[资源 2][1]

选项 #1:使用 React.createRef

class MyComponent extends Component {

    constructor(props) {
        super(props)
        this.myRef = React.createRef() // create a ref object 
    }

    render() {
        return <div ref={this.myRef}></div> // Attach the ref property to a dom element
    }

}

选项#2:使用引用回调

class MyComponent extends Component {

    constructor(props){    // Optional, declare a class field
        super(props)
        this.myRef=null    
    }

    render() {
        return <div ref={ (ref) => this.myRef=ref }></div>
    }   // Attach the dom element to a class field

}

您可以尝试一种更具声明性的方式。我更改了您的代码以反映这一点。您只需要提醒组件会在每次状态/props更改时刷新并调用渲染。因此,我们可以在 render 方法中创建元素的类。

import React from 'react'

export default class myComponent extends React.Component {
  constructor() {
    super();
    this.state = { clicked: false };
    this.handleClick = this.handleClick.bind(this);
  }

  render() {
    let btnClass = 'glyphicon'
    if(this.state.clicked){
      btnClass+=' active'
    }
    return (
      <div>
        <div onClick={this.handleClick}><span ref="btn" className={btnClass}>&nbsp;</span></div>
      </div>
    );
  }

  handleClick() {
    this.setState({
      clicked: !this.state.clicked
    })
  }
}