【问题标题】:React: Function call after render() and before child's constructor()反应:在渲染()之后和孩子的构造函数()之前的函数调用
【发布时间】:2019-06-10 16:12:44
【问题描述】:

我有一个叫Parent的组件,里面还有一个叫Child的组件:

<Parent>
  <Child/>
</Parent>

所以生命周期如下:

  1. 父构造函数
  2. 父级的 render()
  3. 子构造函数
  4. 孩子的 render()
  5. 孩子上车了
  6. 父级已挂载

我可以在第 2 步之后和第 3 步之前以某种方式进行额外的父初始化吗?

更新:

class ThirdPartyLib {
  init(elementId) {
    console.log(`initializing element: ${elementId}`);
    // element with #id: elementId should exist!
    // document.getElementById(elementId).style.color = "red";
  }
}

class Parent extends React.Component {
    constructor(props) {
        super(props);
        console.log("Parent's constructor");
    }

    render() {
        console.log("rendering Parent");
        new ThirdPartyLib().init("parent");
        return (
            <div id="parent">Parent: {this.props.name}
                <Child name="Sara"/>
            </div>
        );
    }

    componentDidMount() {
        console.log("Parent is mounted");
    }
}

class Child extends React.Component {
    constructor(props) {
        super(props);
        console.log(`Child ${this.props.name} constructor`);
    }

    render() {
        console.log(`rendering Child: ${this.props.name}`);
        return <div>Child: {this.props.name}</div>
    }

    componentDidMount() {
        console.log(`Child ${this.props.name} is mounted`);
    }
}

ReactDOM.render(<Parent name="Bob"/>, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>


<div id="app"></div>

在这里我做了一些简化——我不只是改变元素的颜色,我可以在componentDidMount() 方法中实现它。相反,ThirdPartyLib 的细节决定了初始化顺序。在创建任何子元素之前,我必须在父元素出现在 DOM 之后立即对其进行初始化。

更具体地说,ParentChild 共享完全相同的 ThirdPartyLib 类实例。我无法将初始化逻辑放入 Parent 的 render() 函数中,因为该元素尚未在 DOM 中。同样,我无法在 Child 之前通过 componentDidMount() 建议在 cmets 中初始化 Parent,因为 ChildcomponentDidMount()Parent 之前执行。

【问题讨论】:

  • 什么样的初始化?什么函数调用?请更具体并解释您的情况。代码可以放在Parent的render中,但要视情况是否合适。这可能是 XY 问题。
  • 父级的渲染在子级的渲染完成之前无法完成,因为父级的标记是由子级的标记组成的。我不明白这怎么可能。
  • @estus 我正在使用第 3 方库,它需要元素的 #id 作为输入参数。 html 元素由 Parent 的 render() 方法创建。我不能等到父母的componentDidMount() 被执行,因为为时已晚。我也不能将初始化放在 render() 函数中,因为该元素还不存在(在 DOM 中)。
  • 你有 XY 问题。您描述的事情应该发生在 componentDidMount 中。无论问题是什么,都应该从那方面解决。请使用特定代码更新问题。该解决方案可能特定于您使用的库。见stackoverflow.com/help/mcve
  • @estus 查看更新后的问题。

标签: reactjs react-component react-lifecycle


【解决方案1】:

解决此问题的一种方法是将渲染子级延迟到父级挂载之后。步骤如下:

  • 初始父级渲染不渲染子级(例如,在父状态下使用标志抑制)
  • Parent componentDidMount 执行第三方初始化并更改 Parent 状态下的标志,从而触发父级的重新渲染
  • Parent 的重新渲染现在将渲染 Child,并且 Parent 可以通过 prop 将第三方初始化信息传递给 Child

生成的代码类似于:

import React from "react";
import ReactDOM from "react-dom";

class ThirdPartyLib {
  init(elementId) {
    console.log(`initializing element: ${elementId}`);
    this.element = document.getElementById(elementId);
    this.element.style.color = "red";
  }
}

class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { initialized: false };
    console.log("Parent's constructor");
  }

  render() {
    console.log("rendering Parent");
    return (
      <div id="parent">
        Parent: {this.props.name}
        {this.state.initialized && (
          <Child name="Sara" thirdPartyLib={this.state.thirdPartyLib} />
        )}
      </div>
    );
  }

  componentDidMount() {
    console.log("Parent is mounted");
    const thirdPartyLib = new ThirdPartyLib();
    thirdPartyLib.init("parent");
    this.setState({ initialized: true, thirdPartyLib });
  }
}

class Child extends React.Component {
  constructor(props) {
    super(props);
    console.log(`Child ${this.props.name} constructor`);
    console.log(
      `Child knows stuff from thirdPartyLib: ${
        this.props.thirdPartyLib.element.id
      }`
    );
  }

  render() {
    console.log(`rendering Child: ${this.props.name}`);
    return (
      <div>
        Child: {this.props.name}
        <br />
        ThirdPartyLib element id:
        {this.props.thirdPartyLib.element.id}
      </div>
    );
  }

  componentDidMount() {
    console.log(`Child ${this.props.name} is mounted`);
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<Parent name="Bob" />, rootElement);

【讨论】:

  • 太棒了!我会试试你的解决方案
  • @maslick 我刚刚稍微更新了示例以实际利用 Child 中的 thirdPartyLib。
猜你喜欢
  • 1970-01-01
  • 2011-03-06
  • 2015-12-03
  • 2012-09-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多