Spec-Zone.ru › React 17

Перенаправление ссылок

Перенаправление ссылок — это техника автоматического передачи ссылки через компонент одному из его дочерних элементов. Это обычно не требуется для большинства компонентов в приложении. Однако это может быть полезно для некоторых видов компонентов, особенно в библиотеках компонентов повторного использования. Ниже описаны наиболее распространенные сценарии.

Перенаправление ссылок на компоненты DOM

Рассмотрим компонент FancyButton, который отображает нативный элемент DOM button:

function FancyButton(props) {
  return (
    <button className="FancyButton">
      {props.children}
    </button>
  );
}

Компоненты React скрывают свои внутренние детали, включая отображаемый результат. Другие компоненты, использующие FancyButton обычно не будут нуждаться в получении ссылки на внутренний элемент DOM button. Это хорошо, так как предотвращает зависимость компонентов друг от друга слишком сильно от структуры DOM.

Хотя такая инкапсуляция желательна для компонентов прикладного уровня, таких как FeedStory или Comment, она может быть неудобной для высокопереиспользуемых «листовых» компонентов, таких как FancyButton или MyTextInput. Эти компоненты, как правило, используются во всём приложении аналогично обычному элементу DOM button и input, и доступ к их узлам DOM может быть неизбежен для управления фокусом, выбором или анимацией.

Перенаправление ссылок — это опциональная функция, которая позволяет некоторым компонентам принять полученную ими ref и передать её дальше (другими словами, «перенаправить» её) дочернему элементу.

В примере ниже, FancyButton использует React.forwardRef для получения ref, переданной ей, и затем перенаправляет её на элемент DOM button, который она отображает:

const FancyButton = React.forwardRef((props, ref) => (
  <button ref={ref} className="FancyButton">
    {props.children}
  </button>
));

// You can now get a ref directly to the DOM button:
const ref = React.createRef();
<FancyButton ref={ref}>Click me!</FancyButton>;

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

Вот пошаговое объяснение того, что происходит в вышеуказанном примере:

  1. Мы создаём ссылку React, вызывая React.createRef и присваиваем её переменной ref.
  2. Мы передаём нашу ref в <FancyButton ref={ref}>, указав её в качестве атрибута JSX.
  3. React передаёт ref в функцию (props, ref) => ... внутри forwardRef в качестве второго аргумента.
  4. Мы перенаправляем этот аргумент ref в <button ref={ref}>, указав его в качестве атрибута JSX.
  5. Когда ссылка подключается, ref.current будет указывать на узел DOM <button>.

Примечание

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

Перенаправление ссылок не ограничивается компонентами DOM. Вы также можете перенаправлять ссылки на экземпляры компонентов класса.

Примечание для разработчиков библиотек компонентов

Когда вы начинаете использовать forwardRef в библиотеке компонентов, вы должны рассматривать это как изменение, вносящее разрыв, и выпустить новую основную версию вашей библиотеки. Это связано с тем, что ваша библиотека, вероятно, имеет наблюдаемо другое поведение (например, какие ссылки присваиваются и какие типы экспортируются), и это может сломать приложения и другие библиотеки, зависящие от старого поведения.

Условное применение React.forwardRef при его существовании также не рекомендуется по тем же причинам: оно изменяет поведение вашей библиотеки и может сломать приложения ваших пользователей при обновлении самого React.

Перенаправление ссылок в компонентах высшего порядка

Эта техника также может быть особенно полезна при работе с компонентами высшего порядка (также известными как HOC). Начнём с примера HOC, который регистрирует свойства компонента в консоли:

function logProps(WrappedComponent) {
  class LogProps extends React.Component {
    componentDidUpdate(prevProps) {
      console.log('old props:', prevProps);
      console.log('new props:', this.props);
    }

    render() {
      return <WrappedComponent {...this.props} />;
    }
  }

  return LogProps;
}

HOC «logProps» передаёт все props в обернутый компонент, поэтому выходной результат будет таким же. Например, мы можем использовать этот HOC для регистрации всех свойств, которые передаются нашему компоненту «fancy button»:

class FancyButton extends React.Component {
  focus() {
    // ...
  }

  // ...
}

// Rather than exporting FancyButton, we export LogProps.
// It will render a FancyButton though.
export default logProps(FancyButton);

Существует одно предостережение для вышеприведённого примера: ссылки не будут передаваться. Это связано с тем, что ref не является свойством. Как и key, она обрабатывается React по-другому. Если вы добавите ссылку в HOC, ссылка будет ссылаться на внешний контейнерный компонент, а не на обернутый компонент.

Это означает, что ссылки, предназначенные для нашего компонента FancyButton , на самом деле будут привязаны к компоненту LogProps:

import FancyButton from './FancyButton';

const ref = React.createRef();

// The FancyButton component we imported is the LogProps HOC.
// Even though the rendered output will be the same,
// Our ref will point to LogProps instead of the inner FancyButton component!
// This means we can't call e.g. ref.current.focus()
<FancyButton
  label="Click Me"
  handleClick={handleClick}
  ref={ref}/>;

К счастью, мы можем явно перенаправить ссылки на внутренний компонент FancyButton , используя API React.forwardRef. React.forwardRef принимает функцию рендеринга, которая получает параметры props и ref и возвращает узел React. Например:

function logProps(Component) {
  class LogProps extends React.Component {
    componentDidUpdate(prevProps) {
      console.log('old props:', prevProps);
      console.log('new props:', this.props);
    }

    render() {
      const {forwardedRef, ...rest} = this.props;

      // Assign the custom prop "forwardedRef" as a ref
      return <Component ref={forwardedRef} {...rest} />;
    }
  }

  // Note the second param "ref" provided by React.forwardRef.
  // We can pass it along to LogProps as a regular prop, e.g. "forwardedRef"
  // And it can then be attached to the Component.
  return React.forwardRef((props, ref) => {
    return <LogProps {...props} forwardedRef={ref} />;
  });
}

Отображение пользовательского имени в DevTools

React.forwardRef принимает функцию рендеринга. React DevTools используют эту функцию для определения того, что отображать для компонента перенаправления ссылок.

Например, следующий компонент будет отображаться как «ForwardRef» в DevTools:

const WrappedComponent = React.forwardRef((props, ref) => {
  return <LogProps {...props} forwardedRef={ref} />;
});

Если вы назовёте функцию рендеринга, DevTools также будут включать её имя (например, «ForwardRef(myFunction)»):

const WrappedComponent = React.forwardRef(
  function myFunction(props, ref) {
    return <LogProps {...props} forwardedRef={ref} />;
  }
);

Вы даже можете установить свойство displayName функции, чтобы включить компонент, который вы оборачиваете:

function logProps(Component) {
  class LogProps extends React.Component {
    // ...
  }

  function forwardRef(props, ref) {
    return <LogProps {...props} forwardedRef={ref} />;
  }

  // Give this component a more helpful display name in DevTools.
  // e.g. "ForwardRef(logProps(MyComponent))"
  const name = Component.displayName || Component.name;
  forwardRef.displayName = `logProps(${name})`;

  return React.forwardRef(forwardRef);
}
Полезен ли этот сайт?

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

Spec-Zone.ru

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