componentDidMount调用BEFORE引用回调

qui*_*tin 71 javascript reactjs

问题

我正在ref使用内联函数定义设置反应

render = () => {
    return (
        <div className="drawer" ref={drawer => this.drawerRef = drawer}>
Run Code Online (Sandbox Code Playgroud)

那么在componentDidMountDOM引用中没有设置

componentDidMount = () => {
    // this.drawerRef is not defined
Run Code Online (Sandbox Code Playgroud)

我的理解是ref应该在mount期间运行回调,但是在ref回调函数之前调用添加console.log语句.componentDidMount

我看过的其他代码示例,例如github上的这个讨论表明了相同的假设,componentDidMount应该在定义的任何回调之后ref调用render,甚至在对话中说明

因此,在执行了所有的ref回调后,componentDidMount被触发了?

是.

我正在使用反应15.4.1

我试过的其他东西

为了验证ref函数被调用,我尝试在类上定义它

setDrawerRef = (drawer) => {
  this.drawerRef = drawer;
}
Run Code Online (Sandbox Code Playgroud)

然后在 render

<div className="drawer" ref={this.setDrawerRef}>
Run Code Online (Sandbox Code Playgroud)

在这种情况下,控制台日志记录显示回调确实在调用之后 componentDidMount

Dan*_*mov 127

简短回答:

React保证refs在之前设置componentDidMountcomponentDidUpdate挂钩.但仅适用于实际渲染的儿童.

componentDidMount() {
  // can use any refs here
}

componentDidUpdate() {
  // can use any refs here
}

render() {
  // as long as those refs were rendered!
  return <div ref={/* ... */} />;
}
Run Code Online (Sandbox Code Playgroud)

注意这并不意味着"React总是在这些钩子运行之前设置所有引用".
让我们看一些refs 没有设置的例子.


不为未渲染的元素设置参考

React只会为你实际从render返回的元素调用ref回调.

这意味着如果您的代码看起来像

render() {
  if (this.state.isLoading) {
    return <h1>Loading</h1>;
  }

  return <div ref={this._setRef} />;
}
Run Code Online (Sandbox Code Playgroud)

和最初this.state.isLoading就是true,你应该希望this._setRef之前调用componentDidMount.

这应该是有意义的:如果你的第一个渲染返回<h1>Loading</h1>,React就没有办法知道在某些其他条件下它返回需要ref附加的其他东西.还有什么可设置为裁判:<div>,因为没有创建元素render()的方法表示它不应该被渲染.

所以用这个例子,只会componentDidMount发射.但是,this.state.loading更改时false,您将this._setRef首先看到附加,然后componentDidUpdate将触发.


注意其他组件

请注意,如果您将带有refs的子项传递给其他组件,那么他们可能会做一些阻止渲染的事情(并导致问题).

例如,这个:

<MyPanel>
  <div ref={this.setRef} />
</MyPanel>
Run Code Online (Sandbox Code Playgroud)

如果MyPanel不包括props.children在其输出中将无法工作:

function MyPanel(props) {
  // ignore props.children
  return <h1>Oops, no refs for you today!</h1>;
}
Run Code Online (Sandbox Code Playgroud)

同样,它不是一个错误:React没有任何东西可以设置ref,因为没有创建DOM元素.


如果它们被传递给嵌套,则在生命周期之前不会设置引用 ReactDOM.render()

与上一节类似,如果您将带有ref的子项传递给另一个组件,则此组件可能会执行某些操作以防止及时附加ref.

例如,也许它不是从孩子那里返回render(),而是ReactDOM.render()在生命周期钩子中调用.你可以在这里找到一个例子.在该示例中,我们呈现:

<MyModal>
  <div ref={this.setRef} />
</MyModal>
Run Code Online (Sandbox Code Playgroud)

但是在生命周期方法中MyModal执行ReactDOM.render()调用: componentDidUpdate

componentDidUpdate() {
  ReactDOM.render(this.props.children, this.targetEl);
}

render() {
  return null;
}
Run Code Online (Sandbox Code Playgroud)

从React 16开始,生命周期中的这种顶级渲染调用将被延迟,直到整个树的生命周期运行.这可以解释为什么你没有及时看到附件.

此问题的解决方案是使用 门户而不是嵌套ReactDOM.render调用:

render() {
  return ReactDOM.createPortal(this.props.children, this.targetEl);
}
Run Code Online (Sandbox Code Playgroud)

这样我们<div>使用ref实际上包含在渲染输出中.

因此,如果您遇到此问题,则需要验证组件和引用可能会延迟渲染子项的引用之间没有任何内容.

不要setState用来存储裁判

确保你没有setState用于将ref存储在ref回调中,因为它是异步的,在它"完成"之前,componentDidMount将首先执行.


还是个问题?

如果以上提示都没有帮助,请在React中提出问题,我们会看看.

  • 我也对答案做了修改,以解释这种情况。请参阅第一部分。希望这可以帮助! (2认同)