Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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>
        )
    }
})
like image 404
PositiveGuy Avatar asked Aug 17 '16 03:08

PositiveGuy


People also ask

Is render or componentDidMount called first?

componentDidMount. The componentDidMount() method is called after the component is rendered. This is where you run statements that requires that the component is already placed in the DOM.

Does the order of Useeffects matter?

useEffect order But in which order are they called? No surprises here. Everything outside the effects will run first, and then the effects will be called in order. Notice that useEffect accepts a dependency array, which will trigger the effect when the component first mounts and when any of the dependencies change.

What runs first componentDidMount or componentWillMount?

componentDidMount() is only called once, on the client, compared to componentWillMount() which is called twice, once to the server and once on the client. It is called after the initial render when the client received data from the server and before the data is displayed in the browser.


1 Answers

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>
    )
}
like image 178
azium Avatar answered Sep 24 '22 06:09

azium