Home » Reactjs » React “after render” code?

React “after render” code?

Posted by: admin November 29, 2017 Leave a comment

Questions:

I have an app where I need to set the height of an element (lets say “app-content”) dynamically. It takes the height of the “chrome” of the app and subtracts it and then sets the height of the “app-content” to fit 100% within those constraints. This is super simple with vanilla JS, jQuery, or Backbone views, but I’m struggling to figure out what the right process would be for doing this in React?

Below is an example component. I want to be able to set app-content‘s height to be 100% of the window minus the size of the ActionBar and BalanceBar, but how do I know when everything is rendered and where would I put the calculation stuff in this React Class?

/** @jsx React.DOM */
var List = require('../list');
var ActionBar = require('../action-bar');
var BalanceBar = require('../balance-bar');
var Sidebar = require('../sidebar');
var AppBase = React.createClass({
  render: function () {
    return (
      <div className="wrapper">
        <Sidebar />
        <div className="inner-wrapper">
          <ActionBar title="Title Here" />
          <BalanceBar balance={balance} />
          <div className="app-content">
            <List items={items} />
          </div>
        </div>
      </div>
    );
  }
});

module.exports = AppBase;
Answers:

https://facebook.github.io/react/docs/react-component.html#componentdidmount

This method is called once after your component is rendered. So your code would look like so.

var AppBase = React.createClass({
  componentDidMount: function() {
    var $this = $(ReactDOM.findDOMNode(this));
    // set el height and width etc.
  },

  render: function () {
    return (
      <div className="wrapper">
        <Sidebar />
          <div className="inner-wrapper">
            <ActionBar title="Title Here" />
            <BalanceBar balance={balance} />
            <div className="app-content">
              <List items={items} />
          </div>
        </div>
      </div>
    );
  }
});

Questions:
Answers:

One drawback of using componentDidUpdate, or componentDidMount is that they are actually executed before the dom elements are done being drawn, but after they’ve been passed from React to the browser’s DOM.

Say for example if you needed set node.scrollHeight to the rendered node.scrollTop, then React’s DOM elements may not be enough. You need to wait until the elements are done being painted to get their height.

Solution:

Use requestAnimationFrame to ensure that your code is run after the painting of your newly rendered object

scrollElement: function() {
  //store a this ref, and
  var _this = this;
  //wait for a paint to do scrolly stuff
  window.requestAnimationFrame(function() {
    var node = _this.getDOMNode();
    if (node !== undefined) {
      //and scroll them!
      node.scrollTop = node.scrollHeight;
    }
  });
},
componentDidMount: function() {
  this.scrollElement();
},
// and or
componentDidUpdate: function() {
  this.scrollElement();
},
// and or
render: function() {
  this.scrollElement()
  return [...]

Questions:
Answers:

In my experience window.requestAnimationFrame wasn’t enough to ensure that the DOM had been fully rendered / reflow-complete from componentDidMount. I have code running that accesses the DOM immediately after a componentDidMount call and using solely window.requestAnimationFrame would result in the element being present in the DOM; however, updates to the element’s dimensions aren’t reflected yet since a reflow hasn’t yet occurred.

The only truly reliable way for this to work was to wrap my method in a setTimeout and a window.requestAnimationFrame to ensure React’s current call stack gets cleared before registering for the next frame’s render.

function onNextFrame(callback) {
    setTimeout(function () {
        window.requestAnimationFrame(callback)
    }, 0)
}

If I had to speculate on why this is occurring / necessary I could see React batching DOM updates and not actually applying the changes to the DOM until after the current stack is complete.

Ultimately, if you’re using DOM measurements in the code you’re firing after the React callbacks you’ll probably want to use this method.

Questions:
Answers:

From the ReactDOM.render() documentation:

If the optional callback is provided, it will be executed after the
component is rendered or updated.

Questions:
Answers:

I feel that this solution is dirty, but here we go:

componentDidMount() {
    this.componentDidUpdate()
}

componentDidUpdate() {
    // A whole lotta functions here, fired after every render.
}

Now I am just going to sit here and wait for the down votes.

Questions:
Answers:

After render, you can specify the height like below and can specify the height to corresponding react components.

render: function () {
    var style1 = {height: '100px'};
    var style2 = { height: '100px'};

   //window. height actually will get the height of the window.
   var hght = $(window).height();
   var style3 = {hght - (style1 + style2)} ;

    return (
      <div className="wrapper">
        <Sidebar />
        <div className="inner-wrapper">
          <ActionBar style={style1} title="Title Here" />
          <BalanceBar style={style2} balance={balance} />
          <div className="app-content" style={style3}>
            <List items={items} />
          </div>
        </div>
      </div>
    );`
  }

or you can specify the height of the each react component using sass. Specify first 2 react component main div’s with fixed width and then the third component main div’s height with auto. So based on the third div’s content the height will be assigned.

Questions:
Answers:

I’m actually having a trouble with similar behaviour, I render a video element in a Component with it’s id attribute so when RenderDOM.render() ends it loads a plugin that needs the id to find the placeholder and it fails to find it.

The setTimeout with 0ms inside the componentDidMount() fixed it 🙂

componentDidMount() {
    if (this.props.onDidMount instanceof Function) {
        setTimeout(() => {
            this.props.onDidMount();
        }, 0);
    }
}

Questions:
Answers:

A little bit of update with ES6 classes instead of React.createClass

import React, { Component } from 'react';

class SomeComponent extends Component {
  constructor(props) {
    super(props);
    // this code might be called when there is no element avaliable in `document` yet (eg. initial render)
  }

  componentDidMount() {
    // this code will be always called when component is mounted in browser DOM ('after render')
  }

  render() {
    return (
      <div className="component">
        Some Content
      </div>
    );
  }
}

Also – check React component lifecycle methods: https://facebook.github.io/react/docs/react-component.html#the-component-lifecycle

Every component have a lot of methods similar to componentDidMount eg.

  • componentWillUnmount() – component is about to be removed from browser DOM
Questions:
Answers:

React has few lifecycle methods which help in these situations, the lists including but not limited to getInitialState, getDefaultProps, componentWillMount, componentDidMount etc.

In your case and the cases which needs to interact with the DOM elements, you need to wait till the dom is ready, so use componentDidMount as below:

/** @jsx React.DOM */
var List = require('../list');
var ActionBar = require('../action-bar');
var BalanceBar = require('../balance-bar');
var Sidebar = require('../sidebar');
var AppBase = React.createClass({
  componentDidMount: function() {
    ReactDOM.findDOMNode(this).height = /* whatever HEIGHT */;
  },
  render: function () {
    return (
      <div className="wrapper">
        <Sidebar />
        <div className="inner-wrapper">
          <ActionBar title="Title Here" />
          <BalanceBar balance={balance} />
          <div className="app-content">
            <List items={items} />
          </div>
        </div>
      </div>
    );
  }
});

module.exports = AppBase;

Also for more information about lifecycle in react you can have look the below link:
https://facebook.github.io/react/docs/state-and-lifecycle.html

getInitialState, getDefaultProps, componentWillMount, componentDidMount

Questions:
Answers:

I ran into the same problem.

In most scenarios using the hack-ish setTimeout(() => { }, 0) in componentDidMount() worked.

But not in a special case; and I didn’t want to use the ReachDOM findDOMNode since the documentation says:

Note: findDOMNode is an escape hatch used to access the underlying DOM
node. In most cases, use of this escape hatch is discouraged because
it pierces the component abstraction.

(Source: https://facebook.github.io/react/docs/react-dom.html#finddomnode)

So in that particular component I had to use the componentDidUpdate() event, so my code ended up being like this:

componentDidMount() {
    // feel this a little hacky? check this: http://stackoverflow.com/questions/26556436/react-after-render-code
    setTimeout(() => {
       window.addEventListener("resize", this.updateDimensions.bind(this));
       this.updateDimensions();
    }, 0);
}

And then:

componentDidUpdate() {
    this.updateDimensions();
}

Finally, in my case, I had to remove the listener created in componentDidMount:

componentWillUnmount() {
    window.removeEventListener("resize", this.updateDimensions.bind(this));
}