问题
我使用内联函数定义
设置反应ref
render = () => {
return (
<div className="drawer" ref={drawer => this.drawerRef = drawer}>
然后在componentDidMount
中未设置DOM引用
componentDidMount = () => {
// this.drawerRef is not defined
我的理解是应该在mount期间运行ref
回调,但是添加console.log
语句显示componentDidMount
在之前称为回调函数。
我在github上查看的其他代码示例(例如this discussion表示相同的假设,componentDidMount
应该在任何ref
回调之后被称为在render
中定义,它甚至是stated in the conversation
所以在所有引用回调之后,componentDidMount被触发 被执行了吗?
是
我正在使用react 15.4.1
我尝试过的其他事情
为了验证正在调用ref
函数,我尝试在类上定义它
setDrawerRef = (drawer) => {
this.drawerRef = drawer;
}
然后在render
<div className="drawer" ref={this.setDrawerRef}>
此案例中的控制台记录显示回调确实在 componentDidMount
之后被称为
答案 0 :(得分:101)
简短回答:
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={/* ... */} />;
}
注意这并不意味着“在这些钩子运行之前,React始终设置所有引用”。 让我们看一下refs 没有设置的一些例子。
React只会为实际从渲染返回的元素调用ref回调。
这意味着如果您的代码看起来像
render() {
if (this.state.isLoading) {
return <h1>Loading</h1>;
}
return <div ref={this._setRef} />;
}
且最初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>
如果MyPanel
在其输出中未包含props.children
,将无法使用
function MyPanel(props) {
// ignore props.children
return <h1>Oops, no refs for you today!</h1>;
}
同样,这不是一个错误: React没有任何内容可以设置引用,因为没有创建DOM元素。
如果将生命周期传递给嵌套的ReactDOM.render()
,与上一节类似,如果您将带有ref的子项传递给另一个组件,则该组件可能会执行某些操作以防止及时附加ref。
例如,也许它不是从render()
返回孩子,而是在生命周期钩子中调用ReactDOM.render()
。您可以找到此here的示例。在该示例中,我们呈现:
<MyModal>
<div ref={this.setRef} />
</MyModal>
但是MyModal
在 ReactDOM.render()
生命周期方法中执行componentDidUpdate
调用:
componentDidUpdate() {
ReactDOM.render(this.props.children, this.targetEl);
}
render() {
return null;
}
从React 16开始,生命周期中的这种顶级渲染调用将被延迟,直到整个树的生命周期运行。这可以解释为什么你没有及时看到附件。
此问题的解决方案是使用
portals代替嵌套ReactDOM.render
来电:
render() {
return ReactDOM.createPortal(this.props.children, this.targetEl);
}
这样我们带有ref的<div>
实际上包含在渲染输出中。
因此,如果您遇到此问题,则需要验证组件与参考之间没有任何内容可能会延迟渲染子项。
setState
来存储引用确保您没有使用setState
将ref存储在ref回调中,因为它是异步的,在它“完成”之前,componentDidMount
将首先执行。
如果以上提示都没有帮助,请在React中提出问题,我们会看看。
答案 1 :(得分:0)
对问题的不同观察。
我意识到问题只发生在开发模式下。
经过更多调查后,我发现在我的Webpack配置中禁用react-hot-loader
可以防止出现此问题。
我正在使用
这是一个电子应用程序。
我的部分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崩溃(在componentDidMount中未定义ref)
class MyComponent {
constructor (props) {
super(props)
this.inputRef = this.inputRef.bind(this)
}
inputRef (input) {
this.inputField = input
}
render () {
return (
<input ref={this.inputRef}/>
)
}
}
说实话,热重装通常会成为“正确”的问题。使用dev工具快速更新,每个项目都有不同的配置。 也许我的特定配置可以修复。如果是这样的话,我会告诉你。
答案 2 :(得分:0)
当您尝试使用未安装组件的ref时,例如在setinterval中使用ref并且在组件卸载期间未清除设置间隔时,也会出现此问题。
componentDidMount(){
interval_holder = setInterval(() => {
this.myref = "something";//accessing ref of a component
}, 2000);
}
总是清除间隔,例如
componentWillUnmount(){
clearInterval(interval_holder)
}
答案 3 :(得分:-1)
在componentDidMount中,你能在浏览器DOM中找到你的ref元素(div.drawer)吗?如果没有,你就无法参考。由于问题出现在另一个更大的代码中,原因可能是没有呈现ref元素(div.drawer)。