Sometimes when working with React, you will need direct access to the underlying DOM nodes that you render. You might want to measure a node or get the scroll position. Or maybe you need to interoperate with a non-React library that modifies the DOM directly. React provides an escape hatch called
refs for this.
React components have a special
ref prop. You can pass a callback function as the
ref, which will get called with the component instance after the initial render. You can save the reference for use within React lifecycle methods. The instance will either be a custom component or a DOM node. In either case, you can call methods on this instance. Let's try using a ref to measure a DOM node.
There are quite a few things going on here, so let's look at them one at a time.
Here we should pass a callback function that can save the reference to the instance. Most likely, we want to assign the reference to our component, so we need to make sure this function is bound to the current component (we want
this to be our component). We should give the reference a descriptive name (in this case,
We want to measure our rendered node after both the initial render and subsequent renders. To do this, we will measure the node in both
We now have a reference to a DOM node, so we can get its width and height. We want to store these and use them in our render function. The easiest place to store these is in the component's
state, so we'll use
setState. This triggers a second render. Now we can grab the dimensions from
state and display them.
However, after our second render, the content will be different, so the node will need to be measured again. This means
componentDidUpdate is called, which calls
measure, which calls
setState again, thus causing an infinite loop.
Preventing infinite loops with
We can prevent the infinite loop by telling our component to only render if
height has changed.
Here's what'll happen now: after the first renderer, we'll store an initial
setState. This will trigger a second render to display the
height, which will change the
height, causing a third render. The third render will display the latest
height, and hopefully this won't change the dimensions yet again. If it does, we can still run into an infinite loop.
Ultimately, we have to deal with this sort of thing on a case-by-case basis-since there's no perfect solution. It's also important to note that if the render takes long enough, the user may see the initial unmeasured version of the component.