简体   繁体   English

在子支架上访问父组件的子组件中的Ref

[英]Access parent's Ref in child component on child mount

I have the following Parent/Child components. 我具有以下父/子组件。 I need to scroll to top as soon as the Child component is initially rendered. 最初渲染Child组件后,我需要滚动到顶部。

import React, {Component} from 'react';
import Child from "./Child";

class Parent extends Component {

  render() {
    return (
      <div>
        <div ref={topDiv => {
          this.topDiv = topDiv;
        }}/>
        <Child
          topDiv={this.topDiv}
        />
      </div>
    );
  }
}

export default Parent;
import React, {Component} from 'react';

class Child extends Component {

  componentDidMount() {
    this.scrollToTop();
  }

  // componentWillReceiveProps(nextProps, nextContext) {
  //   nextProps.topDiv.scrollIntoView();
  // }

  scrollToTop() {
    this.props.topDiv.scrollIntoView();
  }

  render() {
    return (
      <div style={{height: '500rem'}}>
        Long Child
      </div>
    );
  }
}

export default Child;

Using this, I get the error: 使用此,我得到错误:

TypeError: Cannot read property 'scrollIntoView' of undefined TypeError:无法读取未定义的属性“ scrollIntoView”

I think this is because when componentDidMount is called, the props have not been received yet, so topDiv is null/undefined. 我认为这是因为调用componentDidMount时,尚未收到道具,因此topDiv为null / undefined。

If I use componentWillReceiveProps as shown in the commented part, it works fine. 如果我使用注释部分所示的componentWillReceiveProps ,则可以正常工作。 But I can't use it because: 但我不能使用它,因为:
1. It is deprecated. 1.不推荐使用。
2. I think it will be called every time props are received. 2.我认为每次收到道具时都会调用它。 So I guess I'll need to keep a variable to know if it's the first time props are being received? 因此,我想我需要保留一个变量以了解是否是第一次收到道具?

I can't use componentDidUpdate because the documentation says " This method is not called for the initial render. ". 我不能使用componentDidUpdate因为文档 显示初始渲染未调用此方法。 ”。

How to do something when props are received the first time after a component is rendered? 渲染组件后首次收到道具时该怎么办?

Props are definitely supposed to be available in componentDidMount . 道具绝对应该在componentDidMount可用。

This seems like it could be stemming from some messiness from the React lifecycle. 看来这可能是由于React生命周期中有些混乱。

In your parent, you're providing a ref callback in a render at approximately the same time you're mounting Child . 在父级中,大约在安装Child的同时,在渲染器中提供ref回调。 According to the React docs : 根据React docs

React will call the ref callback with the DOM element when the component mounts, and call it with null when it unmounts. 当组件安装时,React将使用DOM元素调用ref回调,而在卸载时使用null调用它。 Refs are guaranteed to be up-to-date before componentDidMount or componentDidUpdate fires. 保证在componentDidMount或componentDidUpdate触发之前,引用是最新的。

However, componentDidMount of the children will fire before the componentDidMount of their parent, so this guarantee doesn't mean Parent 's this.topDiv will be defined before Child calls componentDidMount . 但是,子级的componentDidMount将在其父级的componentDidMount之前触发,因此,此保证并不意味着Parentthis.topDiv将在Child调用componentDidMount之前定义。 (And, thinking on it further, it definitely doesn't guarantee that it'll be defined before being provided to Child as a prop.) (而且,进一步考虑,它绝对不能保证在将其作为道具提供给Child之前将其定义。)

In your parent, you could maybe try something like 在您父母的身边,您可以尝试类似

componentDidMount() {
    this.setState({ divSet: true });
}

render() {
    let child = null;
    if (this.state.divSet) {
        child = <Child topDiv={this.topDiv} />
    }

    return (
      <div>
        <div ref={topDiv => {
          this.topDiv = topDiv;
        }}/>
        {child}
      </div>
    );
  }

This will guarantee that your ref is set before Child mounts. 这将确保您的ref在Child挂载之前被设置。 The setState is specifically used to force the parent to rerender when the ref is set. setState专门用于在设置ref时强制父级重新呈现。

What you are trying to do is by definition, impossible! 根据定义,您正在尝试做的事情是不可能的! When you say 当你说

I need to scroll to top as soon as the Child component is initially rendered. 最初渲染Child组件后,我需要滚动到顶部。

you mean the Parent's top right? 你的意思是父母的右上角?

But the key detail here is 但是这里的关键细节是

As soon as your child has completed rendering, your parent has not yet been rendered! 您的孩子完成渲染后,您的父母还没有渲染! There is no top to scroll to Yet! 没有顶部可以滚动到!


Life Cycles of a component 组件的生命周期

This is the typical lifecycle flow of a component 这是组件的典型生命周期流程

  1. constructor() 构造函数()
  2. componentWillMount() componentWillMount()
  3. render() 渲染()
  4. componentDidMount() componentDidMount()

When it comes to a parent child relationship it will be 当涉及到亲子关系时

  1. [Parent]constructor() [父]构造()
  2. [Parent]componentWillMount() [父] componentWillMount()
  3. [Parent]render() [父]渲染()
    1. [Child]constructor() [儿童]构造()
    2. [Child]componentWillMount() [儿童] componentWillMount()
    3. [Child]render() [儿童]渲染()
    4. [Child]componentDidMount() - Parent does not exist yet [Child] componentDidMount()-父级不存在
  4. [Parent]componentDidMount() [父] componentDidMount()

Also a small note on the newly added lifecycle method in place of componenWillRecieveProps which is 也是关于新添加的生命周期方法的一则小注释,它代替了componenWillRecieveProps

getDerivedStateFromProps(props, state) getDerivedStateFromProps(道具,状态)

getDerivedStateFromProps is invoked right before calling the render method, both on the initial mount and on subsequent updates. 在初始安装和后续更新上,都在调用render方法之前立即调用getDerivedStateFromProps。 It should return an object to update the state, or null to update nothing. 它应该返回一个对象以更新状态,或者返回null则不更新任何内容。


So your best bet is to call the scroll top within your paren't component componentDidMount where you will have access to it's ref. 因此,最好的选择是在您的组件componentDidMount中调用滚动顶部,您可以在其中访问其引用。

If you are still determined to do this in the child component, you will have to use getDerivedStateFromProps in combination with a flag in state to detect whether you have already scrolled to the top at least once. 如果仍然确定要在子组件中执行此操作,则必须结合使用getDerivedStateFromProps和状态标记来检测是否已至少滚动到顶部一次。 This can be a bit efficient and dirty overall. 总体上来说这可能有点高效和肮脏。

Check here for a cool flow diagram of component lifecycle for clarity! 在此处查看组件生命周期的凉爽流程图,以了解其清晰性!

Bind this.scrollToTop to your class component in the constructor : this.scrollToTop绑定到constructor类组件:

class Child extends Component {
  constructor() {
    this.scrollToTop = this.scrollToTop.bind(this);
  }
  ...rest of your component code

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM