JS: React

Теория: Refs

React по своей природе изолирует вас от прямой работы с DOM на 100%. Но нередко при интеграции сторонних компонентов, которые написаны не на React, возникает задача на прямой доступ к DOM. Также подобный механизм нужен для выделения текста, фокусов и проигрывания медиа.

React позволяет сделать это с помощью рефов (refs). Важно понимать, что в нормальной ситуации этот механизм не нужен и следует максимально избегать его использования.

Рассмотрим задачу по фокусировке на поле ввода:

class CustomTextInput extends React.Component {
  handleFocusTextInput = () => {
    // Explicitly focus the text input using the raw DOM API
    console.log(this.textInput);
    this.textInput.current.focus();
  };

  constructor(props) {
    super(props);
    this.textInput = React.createRef();
  }

  render() {
    return (
      <div>
        <input
          type="text"
          ref={this.textInput} />
        <input
          type="button"
          value="Focus the text input"
          onClick={this.handleFocusTextInput}
        />
      </div>
    );
  }
}

const mountNode = document.getElementById('react-root');
const root = ReactDOM.createRoot(mountNode);
root.render(<CustomTextInput />);

Попрактиковаться

ref — это атрибут компонента, значением которого должен быть объект, созданный в конструкторе через функцию React.createRef(). Этот объект, в отличие от остальных данных, которые находятся в props или state, хранится как обычное свойство объекта. Имя свойства можно выбрать произвольно. Свойство current этого объекта даёт доступ к элементу DOM, именно его можно использовать в componentDidMount или componentDidUpdate.

this.<имя свойства>.current хранит внутри себя DOM-элемент того компонента, для которого был установлен ref. В примере выше это input: <input ref={this.textInput} />. DOM-элемент попадает туда (внутрь current) уже после того, как текущий компонент будет встроен в реальный DOM, а значит им можно воспользоваться в указанных выше колбеках componentDidUpdate и componentDidMount.

Ниже приведён пример создания компонента обёртки над популярным jQuery-плагином Chosen.

class Chosen extends React.Component {
  constructor(props) {
    super(props);
    this.selectRef = React.createRef();
  }
  componentDidMount() {
    $(this.selectRef.current).chosen();
  }

  render() {
    return <select ref={this.selectRef}>
      {this.props.items.map(i => <option>{i}</option>)}
    </select>;
  }
}

const items = ['Document', 'Window', 'Body'];

const mountNode = document.getElementById('react-root');
const root = ReactDOM.createRoot(mountNode);
root.render(<Chosen items={items} />);

Попрактиковаться

Рефы также могут использоваться и на самописных компонентах, реализованных как классы.

Использование в реальном мире

С React удобно и легко работать до тех пор, пока мы остаёмся в рамках самого React, но большая часть существующих JS-библиотек взаимодействует с DOM напрямую, что фактически нивелирует преимущества React при их использовании. Например:

// https://github.com/kylefox/jquery-modal
$('#login-form').modal()

Включение в проект таких библиотек неизбежно приведёт к активному использованию методов жизненного цикла и сделает код сложным. По этой причине принято создавать так называемые компоненты-обёртки (врапперы), которые скрывают внутри себя все взаимодействие с DOM и наружу выставляют стандартный интерфейс React, а именно пропсы. Одной из таких задач, с взаимодействием с DOM, является изменение размера контейнера. Один из вариантов решения — компонент react-resizable. Посмотрите на работу этого компонента:

const Resizable = require('react-resizable').Resizable; // or,
const ResizableBox = require('react-resizable').ResizableBox;

// ES6
import { ResizableBox } from 'react-resizable';

// ...
render() {
  return (
    <ResizableBox width={200} height={200} minConstraints={[100, 100]} maxConstraints={[300, 300]}>
      <span>Contents</span>
    </ResizableBox>
  );
}

Ничего в этом коде не напоминает о реальном DOM. Всё сводится к тому, что компонент оборачивается в ResizableBox, который скрывает всю работу внутри себя. По такому же принципу устроены сотни и, может быть, тысячи других компонентов, которые доступны на GitHub. Вот некоторые из них:

Завершено

0 / 30