为什么 ref='string' 是“inheritance”?

IT技术 reactjs
2021-04-10 23:29:32

在 React 文档中,他们说:

React 还支持在任何组件上使用字符串(而不是回调)作为 ref 属性,尽管这种方法在这一点上主要是遗留的

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);
  }
}

?

第二个例子似乎更干净、更容易。
字符串方法是否有被弃用的风险?


注意:我正在寻找文档中声明的“官方”答案,我不是在询问个人偏好等。

2个回答

虽然可能更简单,但旧的 refs API 在某些边缘情况下可能会变得困难,例如在回调中使用时。所有类型的静态分析对于字符串也是一种痛苦。基于回调的 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          */}
  }
}

可以从问题 #1373 中找到有关基于字符串的 api 的可能问题的进一步讨论和更全面的列表,其中引入了基于回调的 api。我将在此处列出问题描述中的列表:

ref API 损坏有几个方面。

  • 您必须将 this.refs['myname'] 作为字符串引用才能与 Closure Compiler Advanced Mode 兼容。

  • 它不允许单个实例的多个所有者的概念。

  • 神奇的动态字符串可能会破坏 VM 中的优化。

  • 它需要始终一致,因为它是同步解决的。这意味着渲染的异步批处理会引入潜在的错误。

  • 我们目前有一个钩子来获取兄弟引用,这样你就可以让一个组件将它的兄弟引用作为上下文引用。这仅适用于一个级别。这破坏了将其中之一包装在封装中的能力。

  • 它不能是静态类型的。在 TypeScript 之类的语言中,您必须在任何用途中转换它。

  • 在孩子调用的回调中,无法将 ref 附加到正确的“所有者”。<Child renderer={index => <div ref="test">{index}</div>} />-- 此 ref 将附加在发出回调的位置,而不是在当前所有者中。


文档将旧的字符串 API 称为“legacy”,以更清楚地表明基于回调的 API 是首选方法,正如本次提交本 PR中所讨论的,它们实际上是将这些语句放入文档中的第一个地方。另请注意,一些评论暗示基于字符串的 refs api 可能会在某些时候弃用

@GreenAsJade 这个例子有很多我不会在生产代码上做的事情,但由于重点只是演示一个很难用字符串 API 做的模式,我不会太担心。此外,我怀疑那些额外的几毫秒是否会成为任何人的性能瓶颈,除非他们拥有数千个这些组件。
2021-05-26 23:29:32
我想知道您是否可以添加一条注释,指出内联回调是不推荐的性能影响?或者我在这里读到hackernoon.com/refs-in-react-all-you-need-to-know-fb9c9e2aeb81
2021-06-01 23:29:32

最初由 danabramov 在https://news.ycombinator.com/edit?id=12093234上发布

  1. 字符串引用不可组合。如果包装组件已经有一个现有的字符串 ref,则它不能“窥探”到子组件的 ref。另一方面,回调引用没有一个所有者,因此您可以随时组合它们。
  2. 字符串引用不适用于 Flow 等静态分析。Flow 无法猜测框架使字符串 ref “出现”在 上的魔法,this.refs以及它的类型(可能不同)。回调引用对静态分析更友好。
  3. 字符串 ref 的所有者由当前正在执行的组件确定。这意味着使用常见的“渲染回调”模式(例如<DataTable renderRow={this.renderRow} />),错误的组件将拥有 ref(它最终会DataTable代替您的组件定义renderRow)。
  4. 字符串引用强制 React 跟踪当前正在执行的组件。这是有问题的,因为它使reactmodule有状态,因此react在包中复制module时会导致奇怪的错误