Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

resize observer option #27

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 62 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,52 +29,86 @@ function MyComponent() {

## ResizeObserver

[Resize Observer](https://developers.google.com/web/updates/2016/10/resizeobserver)
is the API is used to determine if an element is resized. Browser support is pretty good in Chrome, but is still missing support in other major browsers.
If it is present, this library uses the recent [`ResizeObserver` browser
API](https://developers.google.com/web/updates/2016/10/resizeobserver) to
determine if an element's content size has changed.

If a browser does not have the `ResizeObserver` API present, then this library
falls back to listening on window size changes, which is less efficient and does
not listen for changes to the component's size due to other factors like content
changes. If it is not present, you can use pass a `ResizeObserver`
implementation into the `useComponentSize()` hook (see below).

Browser support is pretty good in Chrome, but is still missing support in other
major browsers.

> [Can i use ResizeObserver?](https://caniuse.com/#feat=resizeobserver)

### Polyfill

You can import the
[polyfill](https://github.com/que-etc/resize-observer-polyfill) directly from here
You can import [a ResizeObserver
ponyfill](https://github.com/que-etc/resize-observer-polyfill) with this NPM
library:

```sh
yarn add resize-observer-polyfill
```

Then import it in your app:
Then use it with the `useComponentSize()` hook:

```js
import 'resize-observer-polyfill'
import ResizeObserver from 'resize-observer-polyfill'
// ...
useComponentSize(ref, { ResizeObserver });
```

If you are using Webpack (or similar) you could use [dynamic
imports](https://webpack.js.org/api/module-methods/#import-), to load the
Polyfill only if needed. A basic implementation could look something like this:
imports](https://webpack.js.org/api/module-methods/#import), to load the
Polyfill only if needed. A basic implementation in your component could look
something like this:

```js
loadPolyfills()
.then(() => /* Render React application now that your Polyfills are ready */)

/**
* Do feature detection, to figure out which polyfills needs to be imported.
**/
function loadPolyfills() {
const polyfills = []

if (!supportsResizeObserver()) {
polyfills.push(import('resize-observer-polyfill'))
// GetResizeObserver.js
// Ponyfill, not polyfill, since we're not chaging the global
// `window.ResizeObserver` variable
let ResizeObserverPonyfill;

export async function getResizeObserverOrPonyfill() {
const BuiltinResizeObserver = window.ResizeObserver;
if (BuiltinResizeObserver) {
return BuiltinResizeObserver;
}

return Promise.all(polyfills)
ResizeObserverPonyfill = (await import("resize-observer-polyfill")).default;
return ResizeObserverPonyfill;
}

function supportsResizeObserver() {
return (
'ResizeObserver' in global &&
'ResizeObserverEntry' in global &&
'contentRect' in ResizeObserverEntry.prototype
)
}
```

```js
// Your component
const [ResizeObserver, setResizeObserver] = useState(
window.ResizeObserver);

useEffect(() => {
if (ResizeObserver) {
return; // don't need to load the ponyfill
}
let cancelled = false;

// if imported multiple times, should load from browser cache;
// or you can cache this in a variable
getResizeObserverOrPonyfill().then(lib => {
if (!cancelled) {
// must wrap `lib` in a function: `ResizeObserver` is a function
// itself, so prevent the state hook from interpreting as a reducer
setResizeObserver(() => lib);
}
});

return () => {
// if unmounted before complete, don't call set state
cancelled = true;
}
}, []);
useComponentSize(ref, { ResizeObserver });
```
6 changes: 5 additions & 1 deletion index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,8 @@ interface ComponentSize {
height: number
}

export default function useComponentSize<T = any>(ref: React.RefObject<T>): ComponentSize
interface ComponentSizeOptions {
ResizeObserver?: ResizeObserver;
}

export default function useComponentSize<T = any>(ref: React.RefObject<T>, opts?: ComponentSizeOptions): ComponentSize
13 changes: 9 additions & 4 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,12 @@ function getSize(el) {
}
}

function useComponentSize(ref) {
function useComponentSize(ref, opts) {
var ResizeObserverConstructor = opts && opts.ResizeObserver
? opts.ResizeObserver
: typeof ResizeObserver === 'function'
? ResizeObserver
: undefined;
var _useState = useState(getSize(ref ? ref.current : {}))
var ComponentSize = _useState[0]
var setComponentSize = _useState[1]
Expand All @@ -40,8 +45,8 @@ function useComponentSize(ref) {

handleResize()

if (typeof ResizeObserver === 'function') {
var resizeObserver = new ResizeObserver(function() {
if (ResizeObserverConstructor) {
var resizeObserver = new ResizeObserverConstructor(function() {
handleResize()
})
resizeObserver.observe(ref.current)
Expand All @@ -58,7 +63,7 @@ function useComponentSize(ref) {
}
}
},
[ref.current]
[ref.current, ResizeObserverConstructor]
)

return ComponentSize
Expand Down
6 changes: 5 additions & 1 deletion index.js.flow
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,8 @@ interface ComponentSize {
height: number,
}

declare export default function useComponentSize<T>(ref: React.Ref<T>): ComponentSize;
interface ComponentSizeOptions {
ResizeObserver?: ResizeObserver;
}

declare export default function useComponentSize<T>(ref: React.Ref<T>, opts?: ComponentSizeOptions): ComponentSize;
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
"raf": "3.4.0",
"react": "16.8.1",
"react-dom": "16.8.1",
"react-test-renderer": "16.8.1"
"react-test-renderer": "16.8.1",
"resize-observer-polyfill": "^1.5.1"
},
"ava": {
"require": [
Expand Down