Spec-Zone.ru › React 17

Справочники и DOM

Справочники предоставляют способ доступа к узлам DOM или элементам React, созданным в методе render.

В типичном потоке данных React, свойства — единственный способ, которым родительские компоненты взаимодействуют со своими дочерними. Чтобы изменить дочерний элемент, его нужно перерисовать с новыми свойствами. Однако есть несколько случаев, когда необходимо императивно изменить дочерний элемент вне типичного потока данных. Дочерний элемент, подлежащий изменению, может быть экземпляром компонента React или элементом DOM. Для обоих этих случаев React предоставляет выход.

Когда использовать справочники

Существует несколько хороших случаев использования справочников:

  • Управление фокусом, выделением текста или воспроизведением медиа.
  • Выполнение императивных анимаций.
  • Интеграция с библиотеками DOM сторонних разработчиков.

Избегайте использования справочников для всего, что можно сделать декларативно.

Например, вместо экспонирования методов open() и close() в компоненте Dialog, передайте ему свойство isOpen.

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

Ваше первое побуждение может состоять в том, чтобы использовать справочники для того, чтобы «заставить происходить действия» в вашем приложении. Если это так, уделите время и более тщательно подумайте о том, где состояние должно принадлежать в иерархии компонентов. Часто становится ясно, что правильное место для «владения» этим состоянием находится на более высоком уровне иерархии. См. руководство Поднятие состояния вверх для примеров этого.

Примечание

Примеры ниже были обновлены для использования API React.createRef(), представленного в React 16.3. Если вы используете более раннюю версию React, мы рекомендуем использовать справочники-обратные вызовы вместо этого.

Создание справочников

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

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef();
  }
  render() {
    return <div ref={this.myRef} />;
  }
}

Доступ к справочникам

Когда справочник передается элементу в render, ссылка на узел становится доступной в атрибуте current справочника.

const node = this.myRef.current;

Значение справочника отличается в зависимости от типа узла:

  • Когда используется атрибут ref на элементе HTML, справочник ref, созданный в конструкторе с помощью React.createRef(), получает основной элемент DOM в качестве свойства current.
  • Когда используется атрибут ref на пользовательском классе компонентов, объект ref получает установленный экземпляр компонента в качестве current.
  • Вы не можете использовать атрибут ref на функциональных компонентах, потому что у них нет экземпляров.

Примеры ниже демонстрируют различия.

Добавление справочника к элементу DOM

Этот код использует ref для хранения ссылки на узел DOM:

class CustomTextInput extends React.Component {
  constructor(props) {
    super(props);
    // create a ref to store the textInput DOM element
    this.textInput = React.createRef();
    this.focusTextInput = this.focusTextInput.bind(this);
  }

  focusTextInput() {
    // Explicitly focus the text input using the raw DOM API
    // Note: we're accessing "current" to get the DOM node
    this.textInput.current.focus();
  }

  render() {
    // tell React that we want to associate the <input> ref
    // with the `textInput` that we created in the constructor
    return (
      <div>
        <input
          type="text"
          ref={this.textInput} />        <input
          type="button"
          value="Focus the text input"
          onClick={this.focusTextInput}
        />
      </div>
    );
  }
}

React присвоит свойство current элементу DOM при подключении компонента и вернет его обратно null при отключении. Обновления ref происходят до componentDidMount или componentDidUpdate циклов жизни.

Добавление справочника к компоненту класса

Если мы захотим обернуть CustomTextInput выше, чтобы смоделировать его немедленное нажатие после подключения, мы можем использовать справочник, чтобы получить доступ к пользовательскому вводу и вручную вызвать его метод focusTextInput:

class AutoFocusTextInput extends React.Component {
  constructor(props) {
    super(props);
    this.textInput = React.createRef();
  }

  componentDidMount() {
    this.textInput.current.focusTextInput();
  }

  render() {
    return (
      <CustomTextInput ref={this.textInput} />
    );
  }
}

Обратите внимание, что это работает только в том случае, если CustomTextInput объявлен как класс:

class CustomTextInput extends React.Component {
  // ...
}

Справочники и функциональные компоненты

По умолчанию вы не можете использовать атрибут ref в функциональных компонентах, так как у них нет экземпляров:

function MyFunctionComponent() {
  return <input />;
}

class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.textInput = React.createRef();
  }
  render() {
    // This will *not* work!
    return (
      <MyFunctionComponent ref={this.textInput} />
    );
  }
}

Если вы хотите разрешить использование ref в вашем функциональном компоненте, вы можете использовать forwardRef (возможно, в сочетании с useImperativeHandle) или преобразовать компонент в класс.

Однако вы можете использовать атрибут ref внутри функционального компонента, если вы ссылаетесь на элемент DOM или компонент класса:

function CustomTextInput(props) {
  // textInput must be declared here so the ref can refer to it
  const textInput = useRef(null);
  
  function handleClick() {
    textInput.current.focus();
  }

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

Экспонирование справочников DOM родительским компонентам

В редких случаях вам может потребоваться получить доступ к узлу DOM дочернего компонента из родительского компонента. Это, как правило, не рекомендуется, так как это нарушает инкапсуляцию компонента, но иногда может быть полезно для запуска фокуса или измерения размера или положения узла DOM дочернего элемента.

Хотя вы могли бы добавить справочник к дочернему компоненту, это не идеальное решение, так как вы получите только экземпляр компонента, а не узел DOM. Кроме того, это не сработает с функциональными компонентами.

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

Если вы используете React 16.2 или более раннюю версию, или если вам нужна большая гибкость, чем предоставляется передачей справочников, вы можете использовать этот альтернативный подход и явно передать справочник как свойство с другим именем.

Если возможно, мы советуем избегать экспонирования узлов DOM, но это может быть полезным выходом. Обратите внимание, что этот подход требует добавления некоторого кода в дочерний компонент. Если у вас абсолютно нет контроля над реализацией дочернего компонента, ваш последний вариант — использовать findDOMNode(), но это не рекомендуется и устарело в StrictMode.

Обратные вызовы справочников

React также поддерживает другой способ установки справочников, называемый «справочниками-обратными вызовами», который предоставляет более точный контроль над временем установки и снятия справочников.

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

В примере ниже реализован общий шаблон: использование обратного вызова ref для хранения ссылки на узел DOM в свойстве экземпляра.

class CustomTextInput extends React.Component {
  constructor(props) {
    super(props);

    this.textInput = null;

    this.setTextInputRef = element => {
      this.textInput = element;
    };

    this.focusTextInput = () => {
      // Focus the text input using the raw DOM API
      if (this.textInput) this.textInput.focus();
    };
  }

  componentDidMount() {
    // autofocus the input on mount
    this.focusTextInput();
  }

  render() {
    // Use the `ref` callback to store a reference to the text input DOM
    // element in an instance field (for example, this.textInput).
    return (
      <div>
        <input
          type="text"
          ref={this.setTextInputRef}        />
        <input
          type="button"
          value="Focus the text input"
          onClick={this.focusTextInput}        />
      </div>
    );
  }
}

React вызовет обратный вызов ref с элементом DOM при подключении компонента и вызовет его с null при отключении. Справочники гарантированно обновляются до componentDidMount или componentDidUpdate.

Вы можете передавать обратные вызовы справочников между компонентами так же, как и со справочниками-объектами, созданными с помощью React.createRef().

function CustomTextInput(props) {
  return (
    <div>
      <input ref={props.inputRef} />
    </div>
  );
}

class Parent extends React.Component {
  render() {
    return (
      <CustomTextInput
        inputRef={el => this.inputElement = el}      />
    );
  }
}

В примере выше, Parent передает свой обратный вызов справочника как свойство inputRef компоненту CustomTextInput, а CustomTextInput передает ту же функцию как специальный атрибут ref компоненту <input>. В результате, this.inputElement в Parent будет установлен на узел DOM, соответствующий элементу <input> в CustomTextInput.

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

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

Примечание

Если вы сейчас используете this.refs.textInput для доступа к справочникам, мы рекомендуем использовать либо паттерн обратного вызова, либо createRef API вместо этого.

Ограничения справочников-обратных вызовов

Если обратный вызов ref определен как встроенная функция, он будет вызываться дважды во время обновлений, сначала с null, а затем снова с элементом DOM. Это происходит потому, что каждый раз при рендеринге создается новый экземпляр функции, поэтому React необходимо очистить старый справочник и настроить новый. Вы можете избежать этого, определив обратный вызов ref как связанный метод в классе, но обратите внимание, что в большинстве случаев это не должно иметь значения.

Полезен ли этот материал?

© 2013–present Facebook Inc.
Licensed under the Creative Commons Attribution 4.0 International Public License.
https://17.reactjs.org/docs/refs-and-the-dom.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API