Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/wellyshen/react-cool-dimensions

😎 📏 React hook to measure an element's size and handle responsive components.
https://github.com/wellyshen/react-cool-dimensions

container-queries dimensions hook measure performance react resizeobserver responsive-components size typescript

Last synced: 8 days ago
JSON representation

😎 📏 React hook to measure an element's size and handle responsive components.

Awesome Lists containing this project

README

        

# REACT COOL DIMENSIONS

A React [hook](https://reactjs.org/docs/hooks-custom.html#using-a-custom-hook) that measure an element's size and handle [responsive components](#responsive-components) with highly-performant way, using [ResizeObserver](https://developer.mozilla.org/en-US/docs/Web/API/Resize_Observer_API). Try it you will 👍🏻 it!

❤️ it? ⭐️ it on [GitHub](https://github.com/wellyshen/react-cool-dimensions/stargazers) or [Tweet](https://twitter.com/intent/tweet?text=With%20@react-cool-dimensions,%20I%20can%20build%20a%20performant%20web%20app.%20Thanks,%20@Welly%20Shen%20🤩) about it.

[![build status](https://img.shields.io/github/workflow/status/wellyshen/react-cool-dimensions/CI?style=flat-square)](https://github.com/wellyshen/react-cool-dimensions/actions?query=workflow%3ACI)
[![coverage status](https://img.shields.io/coveralls/github/wellyshen/react-cool-dimensions?style=flat-square)](https://coveralls.io/github/wellyshen/react-cool-dimensions?branch=master)
[![npm version](https://img.shields.io/npm/v/react-cool-dimensions?style=flat-square)](https://www.npmjs.com/package/react-cool-dimensions)
[![npm downloads](https://img.shields.io/npm/dm/react-cool-dimensions?style=flat-square)](https://www.npmtrends.com/react-cool-dimensions)
[![npm downloads](https://img.shields.io/npm/dt/react-cool-dimensions?style=flat-square)](https://www.npmtrends.com/react-cool-dimensions)
[![gzip size](https://badgen.net/bundlephobia/minzip/react-cool-dimensions?label=gzip%20size&style=flat-square)](https://bundlephobia.com/result?p=react-cool-dimensions)
[![All Contributors](https://img.shields.io/badge/all_contributors-6-orange?style=flat-square)](#contributors-)
[![PRs welcome](https://img.shields.io/badge/PRs-welcome-brightgreen?style=flat-square)](CONTRIBUTING.md)
[![Twitter URL](https://img.shields.io/twitter/url?style=social&url=https%3A%2F%2Fgithub.com%2Fwellyshen%2Freact-cool-dimensions)](https://twitter.com/intent/tweet?text=With%20@react-cool-dimensions,%20I%20can%20build%20a%20performant%20web%20app.%20Thanks,%20@Welly%20Shen%20🤩)

![demo](https://user-images.githubusercontent.com/21308003/91013915-0f18a400-e61b-11ea-9232-291284159cac.gif)

⚡️ Try yourself: https://react-cool-dimensions.netlify.app

## Features

- 🚀 Measures element's size with highly-performant way, using [ResizeObserver](https://developer.mozilla.org/en-US/docs/Web/API/Resize_Observer_API).
- 🎣 Easy to use, based on React [hook](https://reactjs.org/docs/hooks-custom.html#using-a-custom-hook).
- 🍰 Easy to handle [responsive components](#responsive-components), provides an alternative solution to the [container queries](https://wicg.github.io/container-queries) problem.
- 📦 Supports [border-box size measurement](#border-box-size-measurement).
- 🕹 Supports [conditionally updating state](#conditionally-updating-state).
- 🎛 Super flexible [API](#api) design to cover most cases for you.
- 📜 Supports [TypeScript](#working-in-typescript) type definition.
- 🗄️ Server-side rendering compatibility.
- 🦔 Tiny size ([~ 1kB gzipped](https://bundlephobia.com/result?p=react-cool-dimensions)). No external dependencies, aside for the `react`.

## Requirement

To use `react-cool-dimensions`, you must use `[email protected]` or greater which includes hooks.

## Installation

This package is distributed via [npm](https://www.npmjs.com/package/react-cool-dimensions).

```sh
$ yarn add react-cool-dimensions
# or
$ npm install --save react-cool-dimensions
```

## Usage

`react-cool-dimensions` has a flexible [API](#api) design, it can cover simple to complex use cases for you. Here are some examples to show you how does it work.

> ⚠️ [Most modern browsers support ResizeObserver natively](https://caniuse.com/?search=ResizeObserver). You can also use [polyfill](#resizeobserver-polyfill) for full browser support.

### Basic Use Case

To report the size of an element by the `width` and `height` states.

```js
import useDimensions from "react-cool-dimensions";

const App = () => {
const { observe, unobserve, width, height, entry } = useDimensions({
onResize: ({ observe, unobserve, width, height, entry }) => {
// Triggered whenever the size of the target is changed...

unobserve(); // To stop observing the current target element
observe(); // To re-start observing the current target element
},
});

return (


Hi! My width is {width}px and height is {height}px

);
};
```

> 💡 You don't have to call `unobserve` when the component is unmounted, this hook will handle it for you.

### Responsive Components

We have [media queries](https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries) but those are based on the browser viewport not individual elements. In some cases, we'd like to style components based on the width of a containing element rather than the browser viewport. To meet this demand there's a [proposal](https://wicg.github.io/container-queries) for **container queries**, but it still doesn't exist today...

No worries, `react-cool-dimensions` provides an alternative solution for us! We can activate the **responsive mode** by the `breakpoints` option. It's a width-based solution, once it's activated we can easily apply different styles to a component according to the `currentBreakpoint` state. The overall concept as below.

If you wish to update the state on the breakpoints changed, you can set the `updateOnBreakpointChange` option to `true`.

```js
import useDimensions from "react-cool-dimensions";

const Card = () => {
const { observe, currentBreakpoint } = useDimensions({
// The "currentBreakpoint" will be the object key based on the target's width
// for instance, 0px - 319px (currentBreakpoint = XS), 320px - 479px (currentBreakpoint = SM) and so on
breakpoints: { XS: 0, SM: 320, MD: 480, LG: 640 },
// Will only update the state on breakpoint changed, default is false
updateOnBreakpointChange: true,
onResize: ({ currentBreakpoint }) => {
// Now the event callback will be triggered when breakpoint is changed
// we can also access the "currentBreakpoint" here
},
});

return (


I'm 😎

I'm 👕



);
};
```

> Note: If the `breakpoints` option isn't set or there's no the defined breakpoint (object key) for a range of width. The `currentBreakpoint` will be empty string.

## Conditionally Updating State

You can use the `shouldUpdate` option to conditionally update the state to reduce unnecessary re-renders as below.

```js
const returnObj = useDimensions({
shouldUpdate: ({ currentBreakpoint, width, height, entry }) => {
// Will only update the state when the target element's width greater than 300px
return state.width > 300;
},
});
```

> Note: When `updateOnBreakpointChange` and `shouldUpdate` are used at the same time, `shouldUpdate` has a higher priority.

## Border-box Size Measurement

By default, the hook reports the `width` and `height` based on the [content rectangle](https://developers.google.com/web/updates/2016/10/resizeobserver#what_is_being_reported) of the target element. We can include the padding and border for measuring by the `useBorderBoxSize` option. Please note, the `width` and `height` states are rely on the [ResizeObserverEntry.borderBoxSize](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry/borderBoxSize) but [it hasn't widely implemented by browsers](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry/borderBoxSize#Browser_compatibility) therefore we need to use [polyfill](#resizeobserver-polyfill) for this feature.

```js
import useDimensions from "react-cool-dimensions";
import { ResizeObserver } from "@juggle/resize-observer";

const App = () => {
const { observe, width, height } = useDimensions({
useBorderBoxSize: true, // Tell the hook to measure based on the border-box size, default is false
polyfill: ResizeObserver, // Use polyfill to make this feature works on more browsers
});

return (


{/* Now the width and height will be: 100px + 10px + 5px = 115px */}
Hi! My width is {width}px and height is {height}px

);
};
```

## How to Share A `ref`?

You can share a `ref` as follows:

```js
import { useRef } from "react";
import useDimensions from "react-cool-dimensions";

const App = () => {
const ref = useRef();
const { observe } = useDimensions();

return (

{
observe(el); // Set the target element for measuring
ref.current = el; // Share the element for other purposes
}}
/>
);
};
```

## Performance Optimization

The `onResize` event will be triggered whenever the size of the target element is changed. We can reduce the frequency of the event callback by activating the [responsive mode](#responsive-components) or implementing our own throttled/debounced function as below. Note that in order to throttle/debounce the function correctly, it will need to be memorized else it will be recreated on every render call.

```js
import { useMemo } from "react";
import _ from "lodash";

const returnObj = useDimensions({
onResize: useMemo(
() =>
_.throttle(() => {
// Triggered once per every 500 milliseconds
}, 500),
[]
),
});
```

## Working in TypeScript

This hook supports [TypeScript](https://www.typescriptlang.org), you can tell the hook what type of element you are going to observe through the [generic type](https://www.typescriptlang.org/docs/handbook/generics.html):

```ts
const App = () => {
const { observe } = useDimensions();

return

;
};
```

> 💡 For more available types, please [check it out](src/react-cool-dimensions.d.ts).

## API

```js
const returnObj = useDimensions(options?: object);
```

### Return object

It's returned with the following properties.

| Key | Type | Default | Description |
| ------------------- | -------------- | ------- | ---------------------------------------------------------------------------------------------------------------------- |
| `observe` | function | | To set a target element for measuring or re-start observing the current target element. |
| `unobserve` | function | | To stop observing the current target element. |
| `width` | number or null | null | The width of the target element in pixel. Null while target has not mounted. |
| `height` | number or null | null | The height of the target element in pixel. Null while target has not mounted.Z |
| `currentBreakpoint` | string | | Indicates the current breakpoint of the [responsive components](#responsive-components). |
| `entry` | object | | The [ResizeObserverEntry](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry) of the target element. |

### Parameter

The `options` provides the following configurations and event callback for you.

| Key | Type | Default | Description |
| -------------------------- | -------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `breakpoints` | object | | Activates the responsive mode for [responsive components](#responsive-components) or [performance optimization](#performance-optimization). |
| `updateOnBreakpointChange` | boolean | `false` | Tells the hook to update the state on breakpoint changed. |
| `useBorderBoxSize` | boolean | `false` | Tells the hook to [measure the target element based on the border-box size](#border-box-size-measurement). |
| `shouldUpdate` | function | | Tells the hook to [conditionally update the state](#conditionally-updating-state). |
| `onResize` | function | | It's invoked whenever the size of the target element is changed. But in [responsive mode](#responsive-components), it's invoked based on the changing of the breakpoint rather than the size. |
| `polyfill` | ResizeObserver | | It's used for [injecting a polyfill](#resizeobserver-polyfill). |

## ResizeObserver Polyfill

[ResizeObserver has good support amongst browsers](https://caniuse.com/?search=ResizeObserver), but it's not universal. You'll need to use polyfill for browsers that don't support it. Polyfills is something you should do consciously at the application level. Therefore `react-cool-dimensions` doesn't include it.

We recommend using [@juggle/resize-observer](https://github.com/juggle/resize-observer):

```sh
$ yarn add @juggle/resize-observer
# or
$ npm install --save @juggle/resize-observer
```

Then inject it by the `polyfill` option:

```js
import { ResizeObserver } from "@juggle/resize-observer";

const { width, height } = useDimensions(ref, { polyfill: ResizeObserver });
```

Or pollute the `window` object:

```js
import { ResizeObserver, ResizeObserverEntry } from "@juggle/resize-observer";

if (!("ResizeObserver" in window)) {
window.ResizeObserver = ResizeObserver;
// Only use it when you have this trouble: https://github.com/wellyshen/react-cool-dimensions/issues/45
// window.ResizeObserverEntry = ResizeObserverEntry;
}
```

You could use dynamic imports to only load the file when the polyfill is required:

```js
(async () => {
if (!("ResizeObserver" in window)) {
const module = await import("@juggle/resize-observer");
window.ResizeObserver = module.ResizeObserver;
// Only use it when you have this trouble: https://github.com/wellyshen/react-cool-dimensions/issues/45
// window.ResizeObserverEntry = module.ResizeObserverEntry;
}
})();
```

## Articles / Blog Posts

> 💡 If you have written any blog post or article about `react-cool-dimensions`, please open a PR to add it here.

- Featured on [React Status #191](https://react.statuscode.com/issues/191).

## Contributors ✨

Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):



Welly
Welly

💻 📖 🚧
Runar Kristoffersen
Runar Kristoffersen

📖 💻 🤔
Ricardo Amaral
Ricardo Amaral

💻
Cornelius
Cornelius

🐛
Joseph Horton
Joseph Horton

📖
sirkrisp
sirkrisp

💻

This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!