Почему ref = 'string' является "наследием"?

В документации React говорится:

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

https://facebook.github.io/react/docs/more-about-refs.html

Возьмем следующий пример:

class Foo extends Component {
  render() {
    return <input onClick={() => this.action()} ref={input => this._input = input} />;
  }
  action() {
    console.log(this._input.value);
  }
}

Почему я предпочитаю это, а не:

class Foo extends Component {
  render() {
    return <input onClick={() => this.action()} ref='input' />;
  }
  action() {
    console.log(this.refs.input.value);
  }
}

?

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


NB. Я ищу "официальный" ответ на выражение в документации, я не спрашиваю о личных предпочтениях и т.д.

Ответ 1

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

class Repeat extends React.Component {
  render() {
    return <ul> {
      [...Array(+this.props.times)].map((_, i) => {
        return <li key={i}> { this.props.template(i)    } </li>
      })
    } </ul>
  }
}

class Hello extends React.Component {
  constructor() {
    super();
    this.refDict = {};
  }

  render() {
    return <Repeat times="3" template={i => <span ref= {el => this.refDict[i] = el}> Hello {i} </span>} />
           {/*                                    ^^^ Try doing this with the string API          */}
  }
}

Дальнейшее обсуждение и более подробный список возможных проблем с api на основе строк можно найти из issue # 1373, где обратный вызов основанный api. Я включу здесь список из описания проблемы:

API-интерфейс ref нарушен, это несколько аспектов.

  • Вы должны ссылаться на this.refs ['myname'] как строки для совместимости с совместимым компилятором Closure.

  • Это не допускает понятия нескольких владельцев одного экземпляра.

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

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

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

  • Его нельзя статически напечатать. Вы должны использовать его при любом использовании на таких языках, как TypeScript.

  • Нет способа привязать ref к правильному "владельцу" в обратном вызове, вызванном дочерним элементом. <Child renderer={index => <div ref="test">{index}</div>} /> - этот ref будет прикреплен там, где вызывается обратный вызов, а не у текущего владельца.


Документы вызывают устаревший старый API-интерфейс строки, чтобы сделать его более понятным, что API-интерфейс обратного вызова является предпочтительным подходом, как описано в this commit и в этот PR, которые фактически помещают эти заявления в документацию в первую очередь. Также обратите внимание на то, что некоторые из комментариев подразумевают, что в конце строки refs api может быть устаревшим в какой-то момент.

Ответ 2

Первоначально опубликовано danabramov на https://news.ycombinator.com/edit?id=12093234

  • String refs не являются составными. Компонент-оболочка не может "snoop" на ref для ребенка, если он уже имеет существующую строку ref. С другой стороны, обратные вызовы не имеют одного владельца, поэтому вы всегда можете их компилировать.
  • String refs не работает со статическим анализом, таким как Flow. Поток не может угадать магию, которую делает фреймворк, чтобы строка ref появилась на this.refs, а также ее тип (который может быть другим). Обратные обратные ссылки более дружелюбны к статическому анализу.
  • Владелец для строки ref определяется текущим исполняемым компонентом. Это означает, что с общим шаблоном обратного вызова "render callback" (например, <DataTable renderRow={this.renderRow} />) неправильный компонент будет содержать ref (он будет на DataTable вместо вашего компонента, определяющего renderRow).
  • String refs force Реакция на отслеживание текущего исполняемого компонента. Это проблематично, потому что он делает модуль react stateful и, следовательно, вызывает странные ошибки, когда модуль react дублируется в комплекте.