5 Steps: Create a Size & Position Observer in React

siriwatknp
Bits and Pieces
Published in
5 min readNov 12, 2018

--

Have you ever thought about why we have to inspect every time we want to know the width or height of the component? For me, it is really painful and time wasting.

If there are a number of width and height popup on the screen and automatically update when the size changed, It would be absolutely awesome.

Well, that’s why I’m here today. I want to share with you how to create size & position observer in React, using “Render Props” and “Higher Order Component” techniques.

Tip: When working with React components, use Bit to organize and share them in multiple applications and projects- to build faster! Give it a try.

Demo

see source code

Prerequisite

a bit of understanding of these topics.

Get Started

open CodeSandbox in browser, signup if you don’t have an account. It is an amazing tool for prototyping and testing web application.

Step 0

Click create sandbox then choose “React”. It is the same template from the official create-react-app repo.

Add dependencies for styling components. I use react-emotion because it is very easy to write css.

Click Add Dependency and then search for

  • emotion
  • react-emotion
  • cuid

Step 1

Right click at src folder and create file named SizeObserver.js then create simple component that render children

import React from "react";class SizeObserver extends React.Component {
render() {
return this.prop.children;
}
}
export default SizeObserver;

Step 2

add constructor and method getBound()

class SizeObserver extends React.Component {
constructor(props) {
super(props);
this.id = props.name;
}
getBound() {
const component = document.getElementById(this.id);
if (!component) {
return {};
}
const rect = component.getBoundingClientRect();
return {
left: rect.left,
top: rect.top + window.scrollY,
width: rect.width || rect.right - rect.left,
height: rect.height || rect.bottom - rect.top
};
}

render() {
return this.props.children;
}
}

we receive props name from parent that will be a component id. getBoundingClientReact is a method that return an object containing height , width , top , left that we will use in the near future.

Step 3

Now, we will change this.props.children to be a function because it is easier to control and adjust the component that will be tracked.

// SizeObserver.js...render() {
return this.props.children(this.id);
}
...

Test observer in index.js

// index.js...
import styled from 'react-emotion';
const Box = styled("div")({
width: 300,
height: 150,
background: "tomato",
fontSize: "2em",
fontWeight: "bold",
color: "#fff",
lineHeight: "150px"
});
function App() {
return (
<div className="App">
<SizeObserver name="test">
{id => <Box id={id}>Box</Box>}
</SizeObserver
</div>
);
}

Step 4

update observer all the time.

// SizeObserver.js...componentDidMount() {
this.intervalUpdate = setInterval(this.updatePosition, 50);
}
componentWillUnmount() {
clearInterval(this.intervalUpdate);
}
updatePosition = () => {
this.forceUpdate();
};
...render() {
return (
const bound = this.getBound()
console.log(bound)
return this.props.children(this.id);
)
}
...

Now you get the size and position of the component, it is time to display it.

Step 5

create Label component that will display width & height of the Box. You can create a new file, but for demonstration purpose I will add it inside SizeObserver.js .

// SizeObserver.jsconst Label = styled('div')({
position: 'fixed',
background: 'rgba(0,0,0,0.5)',
color: '#fff',
padding: 8,
zIndex: 100,
'*': {
margin: 0,
}
})

Then use Portal to render the Label outside the Box to keep the Box clean. Refactor render method to this

// SizeObserver.js...render() {
const bound = this.getBound()
console.log(bound)
return (
<React.Fragment>
{ReactDOM.createPortal(
<Label
style={{
top: bound.top,
left: bound.left,
}}
>
<p>w: {bound.width}</p>
<p>h: {bound.height}</p>
</Label>,
this.mountNode
)}
{this.props.children(this.id)}
</React.Fragment>
);
}
...

Ta dah!. That’s right baby.

Testing

the most easiest way to test observer is to use css resize: both to the Box

resize: 'both',
overflow: 'auto',
// do not forget to add overflow: auto, otherwise won't work
resizable indicator at the bottom right corner

Drag it to see changes. Tell me it works. Hola, Woo hoo.

Finally

If you want to track multiple components, the code will get ugly because you need to write SizeObserver with children as a function all the time. This is where HOC plays an important role here.

create new file named withSizeObserver.js

I also add cuid() in SizeObserver.js because I want it to auto-gen id if no name from props provided.

This is the result.

That’s all of it. Thank you for reading. I will use SizeObserver for my next story:

Understanding Flex before using it. Stay tuned!

Feel free to comment below and ask anything! I’d be happy to help 👍

Source code for SizeObserver, thank you Code Sandbox.

--

--

Passionate in Design, Theming, React & Firebase. Focus on component reusability.