Javascript componentDidMount 调用 BEFORE ref 回调

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/44074747/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 02:19:51  来源:igfitidea点击:

componentDidMount called BEFORE ref callback

javascriptreactjs

提问by quickshiftin

Problem

问题

I'm setting a react refusing an inline function definition

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

render = () => {
    return (
        <div className="drawer" ref={drawer => this.drawerRef = drawer}>

then in componentDidMountthe DOM reference is not set

然后在componentDidMountDOM 引用中没有设置

componentDidMount = () => {
    // this.drawerRef is not defined

My understanding is the refcallback should be run during mount, however adding console.logstatements reveals componentDidMountis called beforethe ref callback function.

我的理解是ref回调应该在挂载期间运行,但是添加console.log语句显示componentDidMountref 回调函数之前调用的。

Other code samples I've looked at for example this discussionon github indicate the same assumption, componentDidMountshould be called afterany refcallbacks defined in render, it's even stated in the conversation

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

So componentDidMount is fired off after all the ref callbacks have been executed?

Yes.

那么 componentDidMount 在所有 ref 回调都被执行后被触发?

是的。

I'm using react 15.4.1

我正在使用反应15.4.1

Something else I've tried

我尝试过的其他东西

To verify the reffunction was being called, I tried defining it on the class as such

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

setDrawerRef = (drawer) => {
  this.drawerRef = drawer;
}

then in render

然后在 render

<div className="drawer" ref={this.setDrawerRef}>

Console logging in this case reveals the callback is indeed being called aftercomponentDidMount

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

回答by Dan Abramov

Short answer:

简短的回答:

React guarantees that refs are set before componentDidMountor componentDidUpdatehooks. But only for children that actually got rendered.

React 保证在 refscomponentDidMountcomponentDidUpdatehooks之前设置。但仅适用于实际渲染的儿童

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

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

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

Note this doesn't mean “React always sets allrefs before these hooks run”.
Let's look at some examples where the refs don'tget set.

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



Refs don't get set for elements that weren't rendered

不会为未呈现的元素设置 Refs

React will only call ref callbacks for elements that you actually returned from render.

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

This means that if your code looks like

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

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

  return <div ref={this._setRef} />;
}

and initially this.state.isLoadingis true, you should notexpect this._setRefto be called before componentDidMount.

并初步this.state.isLoadingtrue,你应该希望this._setRef之前调用componentDidMount

This should make sense: if your first render returned <h1>Loading</h1>, there's no possible way for React to know that under some other condition it returns something else that needs a ref to be attached. There is also nothing to set the ref to:the <div>element was not created because the render()method said it shouldn't be rendered.

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

So with this example, only componentDidMountwill fire. However, when this.state.loadingchanges to false, you will see this._setRefattached first, and then componentDidUpdatewill fire.

所以在这个例子中,只会componentDidMount触发。但是,this.state.loading更改为 时false,您会先看到this._setRefattached,然后componentDidUpdate才会触发。



Watch out for other components

注意其他组件

Note that if you pass children with refs down to other componentsthere is a chance they're doing something that prevents rendering (and causes the issue).

请注意,如果您将带有 refs 的子组件传递给其他组件,则它们可能会阻止渲染(并导致问题)。

For example, this:

例如,这个:

<MyPanel>
  <div ref={this.setRef} />
</MyPanel>

wouldn't work if MyPaneldid not include props.childrenin its output:

如果MyPanel不包含props.children在其输出中,则不会工作:

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

Again, it's not a bug: there would be nothing for React to set the ref to because the DOM element was not created.

同样,这不是一个错误:React 不会将 ref 设置为因为未创建 DOM 元素



Refs don't get set before lifecycles if they're passed to a nested ReactDOM.render()

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

Similar to the previous section, if you pass a child with a ref to another component, it's possible that this component may do something that prevents attaching the ref in time.

与上一节类似,如果您将带有 ref 的子组件传递给另一个组件,则该组件可能会阻止及时附加 ref。

For example, maybe it's not returning the child from render(), and instead is calling ReactDOM.render()in a lifecycle hook. You can find an example of this here. In that example, we render:

例如,它可能不会从 返回子项render(),而是调用ReactDOM.render()生命周期钩子。您可以在此处找到一个示例。在那个例子中,我们渲染:

<MyModal>
  <div ref={this.setRef} />
</MyModal>

But MyModalperforms a ReactDOM.render()call in itscomponentDidUpdatelifecycle method:

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

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

render() {
  return null;
}

Since React 16, such top-level render calls during a lifecycle will be delayed until lifecycles have run for the whole tree. This would explain why you're not seeing the refs attached in time.

从 React 16 开始,生命周期中的此类顶级渲染调用将被延迟,直到整个树的生命周期都运行完毕。这将解释为什么您没有及时看到附加的参考文献。

The solution to this problem is to use portalsinstead of nested ReactDOM.rendercalls:

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

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

This way our <div>with a ref is actually included in the render output.

这样我们<div>的 ref 实际上包含在渲染输出中。

So if you encounter this issue, you need to verify there's nothing between your component and the ref that might delay rendering children.

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

Don't use setStateto store refs

不要setState用来存储 refs

Make sure you are not using setStateto store the ref in ref callback, as it's asynchronous and before it's "finished", componentDidMountwill be executed first.

确保您没有setState将 ref 存储在 ref 回调中,因为它是异步的,并且在“完成”之前,componentDidMount将首先执行。



Still an Issue?

仍然是一个问题?

If none of the tips above help, file an issue in React and we will take a look.

如果以上提示都没有帮助,请在 React 中提交问题,我们将查看。

回答by Kev

A different observation of the problem.

对问题的不同观察。

I've realised that the issue only occurred while in development mode. After more investigation, I found that disabling react-hot-loaderin my Webpack config prevents this problem.

我意识到这个问题只发生在开发模式下。经过更多调查,我发现react-hot-loader在我的 Webpack 配置中禁用可以防止出现此问题。

I am using

我在用

  • "react-hot-loader": "3.1.3"
  • "webpack": "4.10.2",
  • “反应热加载器”:“3.1.3”
  • "webpack": "4.10.2",

And it's an electron app.

它是一个电子应用程序。

My partial Webpack development config

我的部分 Webpack 开发配置

const webpack = require('webpack')
const merge = require('webpack-merge')
const baseConfig = require('./webpack.config.base')

module.exports = merge(baseConfig, {

  entry: [
    // REMOVED THIS -> 'react-hot-loader/patch',
    `webpack-hot-middleware/client?path=http://localhost:${port}/__webpack_hmr`,
    '@babel/polyfill',
    './app/index'
  ],
  ...
})

It became suspicious when I saw that using inline function in render () was working, but using a bound method was crashing.

当我看到在 render() 中使用内联函数可以正常工作,但使用绑定方法会崩溃时,我开始怀疑了。

Works in any case

在任何情况下都有效

class MyComponent {
  render () {
    return (
      <input ref={(el) => {this.inputField = el}}/>
    )
  }
}

Crash with react-hot-loader(ref is undefined in componentDidMount)

react-hot-loader 崩溃(在 componentDidMount 中未定义引用)

class MyComponent {
  constructor (props) {
    super(props)
    this.inputRef = this.inputRef.bind(this)
  }

  inputRef (input) {
    this.inputField = input
  }

  render () {
    return (
      <input ref={this.inputRef}/>
    )
  }
}

To be honest, hot reload has often been problematic to get "right". With dev tools updating fast, every project has a different config. Maybe my particular config could be fixed. I'll let you know here if that's the case.

老实说,热重载通常很难做到“正确”。随着开发工具的快速更新,每个项目都有不同的配置。也许我的特定配置可以修复。如果是这样的话,我会在这里告诉你。

回答by random coder

The issue can also arise when you try to use a ref of a unmounted component like using a ref in setinterval and do not clear set interval during component unmount.

当您尝试使用未安装组件的引用(例如在 setinterval 中使用引用)并且在组件卸载期间不清除设置间隔时,也会出现此问题。

componentDidMount(){
    interval_holder = setInterval(() => {
    this.myref = "something";//accessing ref of a component
    }, 2000);
  }

always clear interval like for example,

总是清除间隔,例如,

componentWillUnmount(){
    clearInterval(interval_holder)
}