Skip to content
Merged
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
1 change: 1 addition & 0 deletions .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ karma.conf.js
.editorconfig
browserslist
.cache
travis
.travis.yml
tests
tsconfig.json
Expand Down
12 changes: 6 additions & 6 deletions .size-limit.json
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
[
{
"path": "dist/bundle.esm.js",
"limit": "357 B",
"limit": "499 B",
"gzip": true
},
{
"path": "dist/bundle.esm.js",
"limit": "281 B",
"limit": "404 B",
"brotli": true
},
{
"path": "dist/bundle.cjs.js",
"limit": "346 B",
"limit": "485 B",
"gzip": true
},
{
"path": "dist/bundle.cjs.js",
"limit": "261 B",
"limit": "387 B",
"brotli": true
},
{
"path": "polyfilled.js",
"limit": "2678 B",
"limit": "2809 B",
"gzip": true
},
{
"path": "polyfilled.js",
"limit": "2381 B",
"limit": "2512 B",
"brotli": true
}
]
4 changes: 4 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ addons:
firefox: latest
script:
- yarn test
before_deploy:
- export DIST_TAG=$(node travis/getNpmDistTag.js)
- "echo NPM DIST TAG: $DIST_TAG"
deploy:
provider: npm
email: [email protected]
Expand All @@ -19,3 +22,4 @@ deploy:
repo: ZeeCoder/use-resize-observer
branch: master
skip_cleanup: true
tag: ${DIST_TAG}
28 changes: 25 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,42 @@
# CHANGELOG

## 6.2.0-alpha.1

- Only instantiating a ResizeObserver instance if there's actually something to
observe. This for example means that if you pass in `null` or undefined as the
ref, or if neither the default ref or callback ref returned from the hook are
in use, then no ResizeObserver instance will get created until there's an
actual element to observe. Resolves: #42
- The hook now returns `callbackRef`, which can be used in place of the usual
`ref`. Use this instead of a normal ref, when the observed component is
mounted with a delay. Resolves: #43, #45
- The `ref` option now accepts raw elements as well.
- Handling custom refs (through options), the default ref and the callback ref
has been greatly refactored internally (into the `useResolvedElement`
hook), to handle more edge cases with the way refs are handled.
- Tests based on react testing library were refactored to make them much simpler
and more approachable.
- Fixed an error where in certain edge cases the hook tried to set state when
its host component already unmounted.
- Added [contributing guidelines](./CONTRIBUTING.md)
- Overall bundle size increased a bit, due to the new features added.
(With about ~150B or so.)

## 6.1.0

- No changes, only publishing the next minor.

## 6.1.0-alpha3
## 6.1.0-alpha.3

- Fixed SSR rendering, and added a test to cover it.

## 6.1.0-alpha2
## 6.1.0-alpha.2

- ResizeObserver instances are no longer created unnecessarily when the onResize
callback changes. (Fixes #32)
- Written new tests in [react testing library](https://github.com/testing-library/react-testing-library).

## 6.1.0-alpha1
## 6.1.0-alpha.1

- Rewrote the source in TypeScript. (Feedback is welcome.)
- Rewrote tests in TypeScript as well. (Ensures the types make sense.)
Expand Down
37 changes: 37 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# CONTRIBUTING

When contributing to this project, please keep in mind the following goals:

- The hook must remain as simple as possible. It's only a "proxy" to a
ResizeObserver instance, and shouldn't add features that the resize observer
doesn't do.
- All features must be covered with test(s).

It's also best to first submit an issue, before creating a pull request so that
the required feature can be discussed, as well as the actual implementation.

## Adding a New Feature

- Open an issue, so that a discussion can take place,
- Once discussed, add the changes to `src/index.ts` accordingly (make sure TS
types are respected as well),
- Add new test(s) to cover the new feature in: `test/testing-lib.tsx`.
Ignore the other test files, as they're fairly old and much harder to add new
ones to them compared to just using react testing lib.
- Run all the tests to ensure there are no regressions: `yarn test`,

## Using Watch Modes While Developing

There are other test-related scripts in package.json, to enhance the developer
experience.
While making changes you might want to watch the source files, and build them
automatically, as well as having Karma run a (non-headless) Chrome instance
every time a change was made.

To do that:

- Run `yarn src:watch` in a terminal tab
- Run `KARMA_BROWSERS=Chrome yarn karma:watch` in another.

Don't forget to run `yarn test` at the end once you're done with everything, to
make sure the new code is tested for regressions.
105 changes: 92 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@ A React hook that allows you to use a ResizeObserver to measure an element's siz
## Highlights

- Written in **TypeScript**.
- **Tiny**: 353 B (minified, gzipped) Monitored by [size-limit](https://github.com/ai/size-limit).
- **Tiny**: 500B (minified, gzipped) Monitored by [size-limit](https://github.com/ai/size-limit).
- Exposes an **onResize callback** if you need more control.
- [Throttle / Debounce](#throttle--debounce)
- Works with **SSR**.
- Works with **CSS-in-JS**.
- **Supports custom refs** in case you [had one already](#passing-in-your-own-ref).
- Handles many edge cases you might not even think of.
(See this documentation and the test cases.)
- [Throttle / Debounce](#throttle--debounce)
- **Tested** in real browsers. (Headless Chrome and Firefox).

## In Action
Expand All @@ -40,12 +42,12 @@ npm install use-resize-observer --save-dev
Note that the default builds are not polyfilled! For instructions and alternatives,
see the [Transpilation / Polyfilling](#transpilation--polyfilling) section.

```js
```tsx
import React from "react";
import useResizeObserver from "use-resize-observer";

const App = () => {
const { ref, width = 1, height = 1 } = useResizeObserver();
const { ref, width = 1, height = 1 } = useResizeObserver<HTMLDivElement>();

return (
<div ref={ref}>
Expand All @@ -60,15 +62,92 @@ const App = () => {
You can pass in your own ref instead of using the one provided.
This can be useful if you already have a ref you want to measure.

```js
const ref = useRef(null);
const { width, height } = useResizeObserver({ ref });
```ts
const ref = useRef<HTMLDivElement>(null);
const { width, height } = useResizeObserver<HTMLDivElement>({ ref });
```

You can even reuse the same hook instance to measure different elements:

[CodeSandbox Demo](https://codesandbox.io/s/use-resize-observer-reusing-refs-buftd)

## Measuring a raw element

There might be situations where you have an element already that you need to measure.
`ref` now accepts elements as well, not just refs, which means that you can do this:

```ts
const { width, height } = useResizeObserver<HTMLDivElement>({
ref: divElement,
});
```

## Observing components mounted with a delay

Often times you might have to wait before you can mount a component.
Unfortunately if you use a ref, the hook will not be able to pick up its "current"
value being changed afterwards. (Which is by design by React.)

To handle this case, you can do one of these three:

- Use the provided callback ref
- Refactor to a component which you can mount after a "loading" concluded with
the hook within it. This means that the hook will be mounted with the measured
element, which means that a regular ref will work fine.
- Use local state to store the custom ref the hook needs to observe, with "null"
as its starting value. Then once loading concluded, set this state to the ref.
This'll make the hook notice a change from null to the ref, where this time the
ref actually has its current value set properly as well.
([See example here](https://codesandbox.io/s/damp-cookies-6bdrg?file=/src/App.js))

Using a callback ref is recommended, and might look like this:

```tsx
const { callbackRef, width, height } = useResizeObserver<HTMLDivElement>();
const [loaded, setLoaded] = useState(false);

// Simulating a loading state.
useEffect(() => {
setTimeout(() => {
setLoaded(true);
}, 2000);
}, []);

if (!loaded) {
return <div>Loading...</div>;
}

return <div ref={callbackRef}></div>;
```

## Using a single hook to measure multiple refs

The hook reacts to ref changes, as it resolves it to an element to observe.
This means that you can freely change the custom `ref` option from one ref to
another and back, and the hook will start observing whatever is set in its options.

## Opting Out of (or Delaying) ResizeObserver instantiation

In certain cases you might want to delay creating a ResizeObserver instance.

You might provide a library, that only optionally provides observation features
based on props, which means that while you have the hook within your component,
you might not want to actually initialise it.

Another example is that you might want to entirely opt out of initialising, when
you run some tests, where the environment does not provide the `ResizeObserver`.

([See discussions](https://github.com/ZeeCoder/use-resize-observer/issues/40))

You can do one of the following depending on your needs:

- Use a callback ref, or provide a custom ref conditionally, only when needed.
The hook will not create a ResizeObserver instance up until there's something
there to actually observe.
- Patch the test environment, and make a polyfill available as the ResizeObserver.
(This assumes you don't already use the polyfilled version, which would switch
to the polyfill when no native implementation was available.)

## The "onResize" callback

By the default the hook will trigger a re-render on all changes to the target
Expand All @@ -78,13 +157,13 @@ You can opt out of this behaviour, by providing an `onResize` callback function,
which'll simply receive the width and height of the element when it changes, so
that you can decide what to do with it:

```js
```tsx
import React from "react";
import useResizeObserver from "use-resize-observer";

const App = () => {
// width / height will not be returned here when the onResize callback is present
const { ref } = useResizeObserver({
const { ref } = useResizeObserver<HTMLDivElement>({
onResize: ({ width, height }) => {
// do something here.
},
Expand Down Expand Up @@ -119,8 +198,8 @@ and height by default.

You can override this behaviour, which could be useful for SSR as well.

```js
const { ref, width = 100, height = 50 } = useResizeObserver();
```ts
const { ref, width = 100, height = 50 } = useResizeObserver<HTMLDivElement>();
```

Here "width" and "height" will be 100 and 50 respectively, until the
Expand All @@ -131,8 +210,8 @@ ResizeObserver kicks in and reports the actual size.
If you only want real measurements (only values from the ResizeObserver without
any default values), then you can just leave defaults off:

```js
const { ref, width, height } = useResizeObserver();
```ts
const { ref, width, height } = useResizeObserver<HTMLDivElement>();
```

Here "width" and "height" will be undefined until the ResizeObserver takes its
Expand Down
3 changes: 3 additions & 0 deletions docs/development.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Development

Various development notes.
1 change: 1 addition & 0 deletions karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ module.exports = function (config) {
const browsers = (process.env.KARMA_BROWSERS || "ChromeHeadless").split(",");

const testFilePattern = "tests/*.tsx";
// const testFilePattern = "tests/testing-lib.tsx";

config.set({
basePath: ".",
Expand Down
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "use-resize-observer",
"version": "6.1.0",
"version": "6.2.0-alpha.1",
"main": "dist/bundle.cjs.js",
"module": "dist/bundle.esm.js",
"types": "dist/index.d.ts",
Expand Down Expand Up @@ -45,9 +45,10 @@
"@babel/preset-env": "^7.7.7",
"@babel/preset-react": "^7.9.4",
"@babel/preset-typescript": "^7.9.0",
"@rollup/plugin-babel": "^5.2.1",
"@rollup/plugin-inject": "^4.0.1",
"@size-limit/preset-small-lib": "^4.4.5",
"@testing-library/react": "^10.0.2",
"@testing-library/react": "^11.0.4",
"@types/karma": "^5.0.0",
"@types/karma-jasmine": "^3.1.0",
"@types/react": "^16.9.34",
Expand All @@ -58,7 +59,7 @@
"karma": "^5.0.1",
"karma-chrome-launcher": "^3.0.0",
"karma-firefox-launcher": "^1.3.0",
"karma-jasmine": "^3.1.1",
"karma-jasmine": "^4.0.1",
"karma-sourcemap-loader": "^0.3.7",
"karma-spec-reporter": "^0.0.32",
"karma-webpack": "^4.0.2",
Expand All @@ -68,9 +69,8 @@
"react": "^16.9.0",
"react-dom": "^16.9.0",
"rollup": "^2.6.1",
"rollup-plugin-babel": "^4.4.0",
"size-limit": "^4.4.5",
"typescript": "^3.8.3"
"typescript": "^4.0.3"
},
"dependencies": {
"resize-observer-polyfill": "^1.5.1"
Expand Down
Loading