WTF WTF - 3 months ago 12
React JSX Question

Controlling Order of React Component Rendering

As you know React components render in parallel. There is no guarantee in order that they finish rendering.

I want to render the following components in this exact order:


  1. InterviewContentMain (because it will render the header . I need this header to render first so that I can act on it in my other two components after rendered. I am going to run some JS on it, etc. later on from those other two component's componentDidMount so that's why I want this to render first)

  2. InterviewContainer (should only render after InterviewContentMain has rendered)

  3. TableOfContents (should only render after InterviewContainer and InterviewContentMain have rendered)



Here is a little more context https://youtu.be/OrEq5X9O4bw

I tried a bunch of stuff as you can see with the lifecycle methods, which is probably totally wrong or not even sure it's what people are even doing for this sort of scenario, but no luck so far.

const Main = Component({
getInitialState() {
console.log("getInitialState being called");
return null;
},
renderMe() {
console.log("changed InterviewContent's state");
this.setState({renderInk: true});
},
componentDidMount() {
console.log("InterviewContentMain mounted");
},
render(){
var company = this.props.company;

return (
<div id="ft-interview-content">
<p className="section-heading bold font-22" id="interview-heading">Interview</p>
<InterviewContent renderMe={this.renderMe} company={company}/>
</div>
)
}
})

export default InterviewContentMain;


const InterviewContent = Component({
componentDidMount() {
console.log("InterviewContent mounted");
},
renderMe() {
console.log("changed InterviewContent's state");
this.setState({subParentRendered: true});
},
render(){
var company = this.props.company;

return (
<div id="interview-content" className="clear-both">
<div className="column-group">
<div className="all-20">
<TableOfContents renderHeader={this.props.renderMe} renderContent={this.renderMe} company={company}/>
</div>
<div className="all-80">
<InterviewContainer company={company}/>
</div>
</div>
</div>
)
}
})

const TableOfContents = Component({
enableInk() {
new Ink.UI.Sticky(el, {topElement: "#interview-heading", bottomElement: "#footer"});
},
componentDidMount() {
console.log("table of contents mounted");
this.renderHeader;
this.renderContent;
enableInk(); // I only want to run this if the Header and the Content have rendered first
},
render(){
return (
<div>
<p className="margin-8"><span className="blue medium"><Link to="/">HOME</Link></span></p>
)
}
})


UPDATE:

here's what I tried. While they all 3 render, I'm still getting times where TableOfContents renders before InterviewContentMain or InterviewContent which means TableOfContent after rendered overlaps
<p className="section-heading bold font-22" id="interview-heading">Interview</p>
instead of rendering underneath it due to the sticky JS I tried to apply in TableOfContents

const InterviewContentMain = Component({
getInitialState() {
console.log("getInitialState being called");
return {rendered: false};
},
componentDidMount() {
console.log("InterviewContentMain mounted")
this.setState({rendered: true});
},
render(){
var company = this.props.company;

return (
<div id="ft-interview-content">
<div className="section-heading bold font-22" id="interview-heading">Interview</div>
{ this.state.rendered && <InterviewContent company={company}/> }
</div>
)
}
})

export default InterviewContentMain;


const InterviewContent = Component({
getInitialState() {
console.log("getInitialState being called");
return {rendered: false};
},
componentDidMount() {
console.log("InterviewContent mounted")
this.setState({rendered: true});
},
render(){
var company = this.props.company;

return (
<div id="interview-content" className="clear-both">
<div className="column-group">
<div className="all-20">
<TableOfContents company={company}/>
</div>
<div className="all-80">
<InterviewContainer company={company}/>
</div>
</div>
</div>
)
}
})

const TableOfContents = Component({
componentDidMount() {
const el = ReactDOM.findDOMNode(this);
new Ink.UI.Sticky(el,{topElement: "#interview-heading", bottomElement: "#footer"});
console.log("TableOfContents mounted");
},
render(){
return (
<div>
<p className="margin-8"><span className="blue medium"><Link to="/">HOME</Link></span></p>
</div>
)
}
})

Answer

If you want to conditionally render a component, wrap it in a conditional (whether a function or an inline boolean expression)

You say:

TableOfContents (should only render after InterviewContainer and InterviewContentMain have rendered)

.. so inside the render method of TableOfContents parent container, check whether some values are true, like you would in an if statement.

render(){
    var company = this.props.company;

    return (
        <div id="interview-content" className="clear-both">
            <div className="column-group">
                <div className="all-20">
                  {this.state.subParentRendered &&
                    <TableOfContents renderHeader={this.props.renderMe}  renderContent={this.renderMe} company={company}/>
                  }
                </div>
                <div className="all-80">
                    <InterviewContainer company={company}/>
                </div>
            </div>
        </div>
    )
}