componentDidMount 调用了 BEFORE ref 回调



>问题

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

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

则在componentDidMount未设置 DOM 引用

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

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

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

所以组件DidMount在所有引用回调都有后被触发 被处决了?

是的。

我正在使用反应15.4.1

我尝试过的其他东西

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

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

然后在render

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

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

简短回答:

React 保证在componentDidMountcomponentDidUpdate钩子之前设置引用。但仅适用于实际被渲染的孩子

componentDidMount() {
// can use any refs here
}
componentDidUpdate() {
// can use any refs here
}
render() {
// as long as those refs were rendered!
return <div ref={/* ... */} />;
}

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


不会为未呈现的元素设置引用

React 只会为你实际从渲染中返回的元素调用 ref 回调。

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

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

并且最初this.state.isLoadingtrue,你不应该期望在componentDidMount之前调用this._setRef

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

因此,对于此示例,只有componentDidMount会触发。但是,this.state.loading更改为false时,您将看到首先附加this._setRef,然后componentDidUpdate将触发。


注意其他组件

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

例如,这个:

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

如果MyPanel的输出中不包含props.children,则不起作用:

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

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


如果将引用传递到嵌套ReactDOM.render()则不会在生命周期之前设置

引用与上一节类似,如果将带有 ref 的子组件传递给另一个组件,则此组件可能会执行阻止及时附加 ref 的操作。

例如,也许它不是从render()返回子项,而是在生命周期钩子中调用ReactDOM.render()。您可以在此处找到示例。在该示例中,我们呈现:

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

MyModal在其componentDidUpdate生命周期方法中执行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 的<div>实际上包含在渲染输出中。

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

不要使用setState来存储引用

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


还是个问题?

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

对问题的不同观察。

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

我正在使用

  • "反应热加载器":"3.1.3">
  • "网络包": "4.10.2",

这是一个电子应用程序。

我的部分 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'
],
...
})

当我看到在 render() 中使用内联函数可以工作,但使用绑定方法崩溃时,它变得可疑。

在任何情况下都有效

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

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

class MyComponent {
constructor (props) {
super(props)
this.inputRef = this.inputRef.bind(this)
}
inputRef (input) {
this.inputField = input
}
render () {
return (
<input ref={this.inputRef}/>
)
}
}

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

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

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

始终清除间隔,例如,

componentWillUnmount(){
clearInterval(interval_holder)
}

相关内容

  • 没有找到相关文章

最新更新