componentDidMount 调用 BEFORE ref 回调 [英] componentDidMount called BEFORE ref callback

查看:36
本文介绍了componentDidMount 调用 BEFORE ref 回调的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题

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

render = () =>{返回 (<div className="drawer" ref={drawer =>this.drawerRef = 抽屉}>

然后在 componentDidMount 中没有设置 DOM 引用

componentDidMount = () =>{//this.drawerRef 没有定义

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

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

<块引用><块引用>

所以 componentDidMount 在所有 ref 回调完成后被触发被处决了吗?

是的.

我正在使用 React 15.4.1

我尝试过的其他东西

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

setDrawerRef = (抽屉) =>{this.drawerRef = 抽屉;}

然后在render

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

解决方案

简答:

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

componentDidMount() {//可以在这里使用任何引用}componentDidUpdate() {//可以在这里使用任何引用}使成为() {//只要那些 refs 被渲染!返回 

;}

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

<小时>

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

React 只会为您实际<​​strong>从渲染返回的元素调用 ref 回调.

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

render() {如果(this.state.isLoading){返回<h1>加载</h1>;}返回 

;}

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

这应该是有道理的:如果你的第一次渲染返回了

Loading

,那么 React 不可能知道在其他一些条件下它返回了其他需要参考附上.也没有什么可以将 ref 设置为:

元素没有被创建,因为 render() 方法说它不应该不会被渲染.

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

<小时>

注意其他组件

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

例如:

<div ref={this.setRef}/></我的面板>

如果 MyPanel 没有在其输出中包含 props.children 将无法工作:

function MyPanel(props) {//忽略 props.childrenreturn <h1>哎呀,今天没有你的参考!</h1>;}

再说一次,这不是一个错误:因为 DOM 元素没有被创建,所以 React 不会设置 ref .

<小时>

如果将引用传递给嵌套的 ReactDOM.render()

,则不会在生命周期之前设置引用

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

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

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

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

componentDidUpdate() {ReactDOM.render(this.props.children, this.targetEl);}使成为() {返回空;}

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

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

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

这样我们的带有 ref 的

实际上包含在渲染输出中.

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

不要使用 setState 来存储引用

确保您没有使用 setState 将 ref 存储在 ref 回调中,因为它是异步的,并且在完成"之前,将首先执行 componentDidMount.><小时>

还有问题吗?

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

Problem

I'm setting a react ref using an inline function definition

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

then in componentDidMount the DOM reference is not set

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

My understanding is the ref callback should be run during mount, however adding console.log statements reveals componentDidMount is called before the ref callback function.

Other code samples I've looked at for example this discussion on github indicate the same assumption, componentDidMount should be called after any ref callbacks defined in render, it's even stated in the conversation

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

Yes.

I'm using react 15.4.1

Something else I've tried

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

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

then in render

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

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

解决方案

Short answer:

React guarantees that refs are set before componentDidMount or componentDidUpdate hooks. But only for children that actually got rendered.

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 all refs before these hooks run".
Let’s look at some examples where the refs don’t get set.


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

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

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.isLoading is true, you should not expect this._setRef to be called before 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.

So with this example, only componentDidMount will fire. However, when this.state.loading changes to false, you will see this._setRef attached first, and then componentDidUpdate will fire.


Watch out for other components

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

For example, this:

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

wouldn't work if MyPanel did not include props.children in its output:

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.


Refs don’t get set before lifecycles if they’re passed to a nested 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.

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:

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

But MyModal performs a ReactDOM.render() call in its componentDidUpdate lifecycle method:

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.

The solution to this problem is to use portals instead of nested ReactDOM.render calls:

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

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

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

Don't use setState to store refs

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


Still an Issue?

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

这篇关于componentDidMount 调用 BEFORE ref 回调的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆