Javascript 在ref回调之前调用componentDidMount

Javascript 在ref回调之前调用componentDidMount,javascript,reactjs,Javascript,Reactjs,问题 我正在使用内联函数定义设置react render = () => { return ( <div className="drawer" ref={drawer => this.drawerRef = drawer}> 我的理解是ref回调应该在装载期间运行,但是添加console.log语句会显示componentDidMount在调用ref回调函数之前调用 我在github上看到的其他代码示例表明了相同的假设,componentDidM

问题

我正在使用内联函数定义设置react

render = () => {
    return (
        <div className="drawer" ref={drawer => this.drawerRef = drawer}>
我的理解是
ref
回调应该在装载期间运行,但是添加
console.log
语句会显示
componentDidMount
在调用ref回调函数之前调用

我在github上看到的其他代码示例表明了相同的假设,
componentDidMount
应该在
render
中定义的任何
ref
回调之后调用,这是偶数

因此,在所有ref回调完成后,componentDidMount将被触发 被处决了

我使用的是react 15.4.1

我试过的其他东西

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

setDrawerRef = (drawer) => {
  this.drawerRef = drawer;
}
然后在
渲染中

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

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

React保证在
componentDidMount
componentDidUpdate
挂钩之前设置引用。但仅适用于实际渲染的儿童

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

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

render() {
  // as long as those refs were rendered!
  return <div ref={/* ... */} />;
}
如果
MyPanel
的输出中不包含
props.children
,则无法工作:

function MyPanel(props) {
  // ignore props.children
  return <h1>Oops, no refs for you today!</h1>;
}
但是
MyModal
在其
componentdiddupdate
生命周期方法中执行
ReactDOM.render()
调用:

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

render() {
  return null;
}
自React 16以来,生命周期中的此类顶级渲染调用将被延迟,直到整个树的生命周期运行为止。这可以解释为什么你没有及时看到裁判

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

render() {
  return ReactDOM.createPortal(this.props.children, this.targetEl);
}
这样,带有ref的
实际上包含在渲染输出中

因此,如果遇到此问题,则需要验证组件和ref之间没有可能延迟渲染子级的内容

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


还是个问题吗?
如果以上提示都没有帮助,请在React中提交一个问题,我们将进行查看

对问题的不同观察

我意识到这个问题只发生在开发模式下。 经过进一步调查,我发现在我的网页配置中禁用
react hot loader
,可以防止这个问题

我正在使用

  • “反应热加载程序”:“3.1.3”
  • “网页包”:“4.10.2”
这是一个电子应用程序

我的部分网页开发配置

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'
  ],
  ...
})
当我看到在render()中使用内联函数可以正常工作,但使用绑定方法会崩溃时,我开始怀疑

在任何情况下都有效

class MyComponent {
  render () {
    return (
      <input ref={(el) => {this.inputField = el}}/>
    )
  }
}
类MyComponent{
渲染(){
返回(
{this.inputField=el}}/>
)
}
}
使用react热加载程序崩溃(componentDidMount中未定义ref)

类MyComponent{
建造师(道具){
超级(道具)
this.inputRef=this.inputRef.bind(this)
}
inputRef(输入){
this.inputField=输入
}
渲染(){
返回(
)
}
}
老实说,热重新加载常常是“正确”的问题。随着开发工具的快速更新,每个项目都有不同的配置。
也许我的特定配置可以修复。如果是这样的话,我会在这里告诉您。

当您尝试使用未安装组件的ref时,也会出现问题,就像在setinterval中使用ref一样,并且在组件卸载期间不清除set interval

componentDidMount(){
    interval_holder = setInterval(() => {
    this.myref = "something";//accessing ref of a component
    }, 2000);
  }
始终清除间隔,例如

componentWillUnmount(){
    clearInterval(interval_holder)
}

我可能错了,但当您使用arrow函数进行渲染方法时,它将从类外的词法范围捕获
this
的值。试着去掉类方法的arrow函数语法,看看它是否有用。@GProst这就是我问题的本质。我将console.log放在两个函数中,componentDidMount首先运行,其次是ref回调。只是有一个类似的问题——对我们来说,基本上,我们在最初的
渲染时错过了它,因此需要利用
componentDidUpdate
,因为
componentDidMount
不是更新的一部分。可能不是您的问题,但认为它可能值得作为一个潜在的解决方案提出。React 16也是如此。文档清楚地说明了
ref回调是在componentDidMount或componentDidUpdate生命周期挂钩之前调用的。
但这似乎不是真的:(1.ref箭头声明是:
ref={ref=>{this.drawerRef=ref}}
2.甚至在componentDidMount之前调用ref;只有在呈现案例中的div时,才能在初始呈现之后访问ref。因此,您必须能够在下一个级别中访问ref,即,在componentWillReceiveProps中使用
this.drawerRef
3。如果您尝试在初始装载之前访问ref,则只能获得未定义的va我也对我的答案进行了编辑,以解释这种情况。请参阅第一节。希望这能有所帮助!嗨@DanAbramov谢谢!不幸的是,当我第一次遇到它时,我无法开发出一个可复制的案例。遗憾的是,我不再从事该项目,并且从那时起就无法重新编写。这个问题已经变得足够流行了hough,我同意这一点,试图找到可复制的案例是关键,因为许多人似乎都遇到了这个问题。我认为在许多情况下,这可能是由于误解造成的。在React 15中,这也可能是由于被吞没的错误造成的(React 16具有更好的错误处理能力,可以防止这种情况发生).当这种情况发生时,我很乐意回顾更多的案例,所以请放心
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'
  ],
  ...
})
class MyComponent {
  render () {
    return (
      <input ref={(el) => {this.inputField = el}}/>
    )
  }
}
class MyComponent {
  constructor (props) {
    super(props)
    this.inputRef = this.inputRef.bind(this)
  }

  inputRef (input) {
    this.inputField = input
  }

  render () {
    return (
      <input ref={this.inputRef}/>
    )
  }
}
componentDidMount(){
    interval_holder = setInterval(() => {
    this.myref = "something";//accessing ref of a component
    }, 2000);
  }
componentWillUnmount(){
    clearInterval(interval_holder)
}