5 Steps: Create a Size & Position Observer in React
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
Prerequisite
a bit of understanding of these topics.
- Render Props
- Portal
- Higher Order Component (optional)
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
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.