Подробное объяснение React Refs

React.js

Прежде чем вводить ссылки, давайте сначала разберемся с двумя понятиями: контролируемые компоненты и неконтролируемые компоненты.

Управляемые компоненты

В HTML элементы формы, такие как input, textarea, select, часто могут сохранять состояние самостоятельно и обновляться на основе ввода пользователя. В React изменяемое состояние обычно хранится в свойстве состояния компонента и может быть обновлено только с помощью setState(). Здесь мы используем состояние React как единственный источник данных для управления действиями, отправляемыми формой во время пользовательского ввода, путем рендеринга компонента React формы. «Элемент ввода формы, значение которого таким образом контролируется React», называетсяУправляемые компоненты.

неконтролируемые компоненты

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

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

В React из-за появления рефовнеконтролируемые компонентыПоддержание собственного значения состояния стало намного проще.Далее мы сосредоточимся на использовании Refs.

Что такое рефы

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

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

сцены, которые будут использоваться

refs вообще подходят для использования в каких сценариях:

  1. Управление фокусом элемента DOM, выбором контента или воспроизведением мультимедиа;
  2. Запускайте эффекты анимации, управляя элементами DOM;
  3. Интеграция со сторонними DOM-библиотеками.

Избегайте использования ссылок для всего, что можно сделать декларативно. Например, избегайте раскрытия open(), show(), hide(), close() и других методов внутри таких компонентов, как Dialog, Loading, Alert и т. д. Лучше всего управлять ими через атрибут isXX.

Как использовать

Существует два способа использования ссылок: 1) через API React.createRef() [введенный после React 16.3] 2) В более ранних версиях мы рекомендуем использовать ссылки в виде обратных вызовов.

React.createRef()

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

Приведенный выше код создает свойство экземпляра this.tRef и добавляет его. Передано в DOM-элемент div. Последующие ссылки на этот узел могут быть доступны в текущем свойстве ref. Значение ref зависит от типа узла:

  1. Когда атрибут ref используется для обычных элементов HTML, ссылка, созданная с помощью React.createRef() в конструкторе, получает базовый элемент DOM в качестве своего текущего атрибута.
class TestComp extends React.Component {
  constructor(props) {
    super(props);
    // 创建一个 ref 来存储 DOM元素 input
    this.textInput = React.createRef();
    this.focusEvent = this.focusEvent.bind(this);
  }
  focusEvent() {
    // 直接通过原生API访问输入框获取焦点事件
    this.textInput.current.focus();
  }
  render() {
    return (
      <div>
        <input type="text" ref={this.textInput} />
        <input type="button" value="获取文本框焦点事件" onClick={this.focusEvent}/>
      </div>
    );
  }
}

  1. Когда атрибут ref используется для пользовательского компонента класса, объект ref получает смонтированный экземпляр компонента в качестве своего текущего атрибута.
class ParentComp extends React.Component {
  constructor(props) {
    super(props);
    // 创建ref 指向 ChildrenComp 组件实例
    this.textInput = React.createRef();
  }

  componentDidMount() {
    // 调用子组件 focusTextInput方法 触发子组件内部 文本框获取焦点事件
    this.textInput.current.focusTextInput();
  }

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

class ChildrenComp extends React.Component {
  constructor(props) {
    super(props);
    this.inputRef = React.createRef();
  }
  focusTextInput() {
    this.inputRef.current.focus();
  }
  render(){
    return(
      <div>
        <input type='text' value='父组件通过focusTextInput()方法控制获取焦点事件' ref={ this.inputRef }/>
      </div>
    )
  }
}

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

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


class TestComp extends React.Component {
  constructor(props) {
    super(props);
    this.textInput = null;
    // 使用'ref'的回调函数将 text输入框DOM节点的引用绑定到 React实例 this.textInput上
    this.inputRef = element => {
      this.textInput = element;
    }
    this.focus = () => {
      if (this.textInput) {
        this.textInput.focus();
      }
    }
  }
  componentDidMount() {
    this.focus();
  }
  render() {
    return (
      <div>
        <input type='text' ref={ this.inputRef } />
      </div>
    );
  }
}

React вызовет обратный вызов ref с элементом DOM, когда компонент смонтирован, и вызовет его с нулевым значением, когда компонент будет размонтирован. React обеспечит актуальность ссылок до того, как сработает componentDidMount или componentDidUpdate. В компоненте класса обычно родительский компонент передает свою функцию обратного вызова refs дочернему компоненту в виде реквизита, а дочерний компонент использует ту же функцию в качестве специального атрибута ref Передается соответствующему элементу DOM.

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

class TestComp extends React.Component {
  constructor(props) {
    super(props);
    this.textInput = null;
    // 初始化 flag 值为 init
    this.state = {
      flag: 'init'
    }
    this.focus = () => {
      if (this.textInput) {
        this.textInput.focus();
      }
    }
  }
  componentDidMount() {
    this.focus();
    // 当执行完 render 首次渲染之后,更新状态 flag 值 为 update
    this.setState({
      flag: 'update'
    });
  }
  render() {
    return (
      <div>
      {/* 通过内联回调形式定义 ref  */}
      <input type='text' value={this.state.flag} ref={(element) => {
        console.log('element', element); // 将传入的 element 输出控制台
        this.textInput = element;
      }} />
      </div>
    )
  }
}

Устаревший API: ссылки типа String

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

Как открыть DOM родительскому компоненту через ссылки

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

В React 16.3 или более поздних версиях мы рекомендуем использовать переадресацию ссылок для выполнения вышеуказанных операций.

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

Ref forwarding is a technique for automatically passing a ref through a component to one of its children. This is typically not necessary for most components in the application. However, it can be useful for some kinds of components, especially in reusable component libraries.

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

const ref = React.createRef();
const BtnComp = React.forwardRef((props, ref) => {
  return (
    <div>
      <button ref={ref} className='btn'>
        { props.children }
      </button>
    </div>
  )
});

class TestComp extends React.Component {
  clickEvent() {
    if (ref && ref.current) {
      ref.current.addEventListener('click', () => {
        console.log('hello click!')
      });
    }
  }
  componentDidMount() {
    console.log('当前按钮的class为:', ref.current.className); // btn
    this.clickEvent(); // hello click!
  }
  render() {
    return (
      <div>
        <BtnComp ref={ref}>点击我</BtnComp>
      </div>
    );
  }
}

В приведенном выше случае используемый компонент BtnComp может получить ссылку на базовый DOM-узел кнопки и при необходимости манипулировать им, точно так же, как обычная кнопка HTML-элемента использует DOM напрямую.

Меры предосторожности

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

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

refs в компонентах более высокого порядка

Компоненты высшего порядка (HOC) — это продвинутая техника в React для повторного использования логики компонентов.Компонент высшего порядка — это функция, которая принимает компонент и возвращает новый компонент. Давайте посмотрим, как ссылки обычно используются в высокоуровневых компонентах в конкретных случаях.


// 记录状态值变更操作
function logProps(Comp) {
  class LogProps extends React.Component {
    componentDidUpdate(prevProps) {
      console.log('old props:', prevProps);
      console.log('new props:', this.props);
    }
    render() {
      const { forwardedRef, ...rest } = this.props;
      return <Comp ref={ forwardedRef } {...rest} />;
    }
  }
  return React.forwardRef((props, ref) => {
    return <LogProps { ...props } forwardedRef={ ref } />;
  });
}

// 子组件
const BtnComp = React.forwardRef((props, ref) => {
  return (
    <div>
      <button ref={ref} className='btn'>
        { props.children }
      </button>
    </div>
  )
});

// 被logProps包装后返回的新子组件
const NewBtnComp = logProps(BtnComp);


class TestComp extends React.Component {
  constructor(props) {
    super(props);
    this.btnRef = React.createRef();

    this.state = {
      value: '初始化'
    }
  }

  componentDidMount() {
    console.log('ref', this.btnRef);
    console.log('ref', this.btnRef.current.className);
    this.btnRef.current.classList.add('cancel'); // 给BtnComp中的button添加一个class
    this.btnRef.current.focus(); // focus到button元素上
    setTimeout(() => {
      this.setState({
        value: '更新'
      });
    }, 10000);
  }

  render() {
    return (
      <NewBtnComp ref={this.btnRef}>{this.state.value}</NewBtnComp>
    );
  }
}

Окончательный рендеринг выглядит следующим образом:


Примечание. Статья взята из публичного аккаунта react_native и воспроизведена с разрешения автора.


В этой статье в основном описывается использование ссылок React. Если вы хотите получить больше контента, связанного с React, отсканируйте код, чтобы подписаться на общедоступную учетную запись WeChat «Tongbanjie Technology», и ответьте «React» или «react-native» в фон, чтобы получить более захватывающий контент.