Подтвердить что ты не робот

Предупреждение об изъятии с использованием this.refs

У меня есть компонент 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" обесценивается.

Что мне делать вместо этого? Как я могу исправить это, чтобы он не использовал устаревший код?

4b9b3361

Ответ 1

Правило Lint, на которое вы ссылаетесь, называется no-string-refs и предупреждает вас:

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

Вы получаете это предупреждение, потому что реализовали устаревший способ использования refs (с помощью строк). В зависимости от вашей версии 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>
  );
}

Реакт 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>
  );
}

Посмотрите, что говорится в официальной документации по ссылкам и DOM, и в частности в этом разделе:

Устаревший API: Строковые ссылки

Если вы работали с React ранее, вы можете быть знакомы с более старым API, в котором атрибут ref является строкой, такой как "textInput", а узел DOM доступен как this.refs.textInput. Мы не советуем этого делать, потому что у строковых ссылок есть некоторые проблемы, они считаются устаревшими и , вероятно, будут удалены в одном из будущих выпусков. Если вы сейчас используете this.refs.textInput для доступа к this.refs.textInput, мы рекомендуем вместо этого использовать шаблон обратного вызова.

Ответ 2

вы можете попробовать более декларативный способ. Я изменил ваш код, чтобы отразить это. Вам просто нужно напомнить, что компонент обновит и вызовет рендер в каждом изменении состояния/реквизита. Таким образом, мы можем создать класс вашего элемента внутри метода 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
    })
  }
}

Ответ 3

Причина, по которой существует это правило ESLint, заключается в том, что строки Refs находятся на выходе. Однако для приведенного выше кода я бы рекомендовал не использовать Ref в первую очередь.

Не злоупотребляйте ссылками

Преимущество реагирования в том, что оно носит декларативный характер. Это означает, что у нас есть состояние и выражение (возвращенное JSX) того, как должен выглядеть пользовательский интерфейс (точнее, DOM) при определенном состоянии.

Все, что можно сделать с помощью только выражения состояния и пользовательского интерфейса, должно быть сделано таким образом. Проблема использования Ref в приведенном выше коде заключается в том, что он делает код обязательным. Мы не можем понять, как будет выглядеть DOM только с JSX. Вот как вы можете достичь того же результата декларативным способом:

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

}

Ссылки должны использоваться, когда состояния и выражения пользовательского интерфейса недостаточно, и вам нужен доступ к фактическому DOM. Например, фокусировка на поле ввода, прокрутка к элементу или получение точной ширины и высоты элемента.

Если вы используете ссылки, избегайте строковых ссылок

Строковые ссылки вредят производительности, не являются композируемыми и находятся там.

Строковые ссылки имеют некоторые проблемы, считаются устаревшими и, вероятно, будут удалены в одном из будущих выпусков. [Официальная документация React]

[resource1] [1], [resource2] [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

}