Reputation: 3360
I have a component called Parent, inside of which there is another component named Child:
<Parent>
<Child/>
</Parent>
So the lifecycle is as follows:
Can I do additional Parent initialisation after step 2 and before step 3 somehow?
UPDATE:
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>
Here I made some simplifications - I am not merely changing the element's color, I could have made it in the componentDidMount()
method. Rather, the ThirdPartyLib
specifics dictate the initialization sequence. I have to initialize the Parent right after it appears in the DOM, before any child element is created.
To be more specific, Parent
and Child
share the exact same instance of the ThirdPartyLib
class. I cannot put the initialization logic into Parent's render()
function, since the element is not in the DOM yet. Likewise, I cannot initialize the Parent
before the Child
as suggested in the comments via componentDidMount()
, since the Child
's componentDidMount()
is executed before the Parent
's.
Upvotes: 3
Views: 6360
Reputation: 81006
One way to approach this is to delay rendering the child until after mount of the parent. The steps would look as follows:
componentDidMount
performs third-party initialization and changes flag in Parent state triggering a re-render of the parentThe resulting code would be something like:
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);
Upvotes: 6