UNPKG

10.4 kBMarkdownView Raw
1# Handle element resizes like it's 2022!
2
3<img src="https://img.shields.io/npm/dm/react-resize-detector?style=flat-square"> <img src="https://badgen.net/bundlephobia/minzip/react-resize-detector?style=flat-square"> <img src="https://badgen.net/bundlephobia/tree-shaking/react-resize-detector?style=flat-square">
4
5#### [Live demo](http://maslianok.github.io/react-resize-detector/)
6
7Nowadays browsers support element resize handling natively using [ResizeObservers](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver). The library uses these observers to help you handle element resizes in React.
8
9🐥 Tiny <a href="https://bundlephobia.com/result?p=react-resize-detector" target="__blank">~3kb</a>
10
11🐼 Written in TypeScript
12
13🦁 Supports Function and Class Components
14
15🐠 Used by <a href="https://github.com/maslianok/react-resize-detector/network/dependents" target="__blank">20k+ repositories</a>
16
17🦄 Generating 35M+ downloads/year
18
19No `window.resize` listeners! No timeouts! No 👑 viruses! :)
20
21<i>TypeScript-lovers notice: starting from v6.0.0 you may safely remove `@types/react-resize-detector` from you deps list.</i>
22
23## Installation
24
25```ssh
26npm i react-resize-detector
27// OR
28yarn add react-resize-detector
29```
30
31and
32
33```jsx
34import ResizeObserver from 'react-resize-detector';
35```
36
37## Examples
38
39Starting from v6.0.0 there are 3 recommended ways to work with `resize-detector` library:
40
41#### 1. React hook (new in v6.0.0)
42
43```jsx
44import { useResizeDetector } from 'react-resize-detector';
45
46const CustomComponent = () => {
47 const { width, height, ref } = useResizeDetector();
48 return <div ref={ref}>{`${width}x${height}`}</div>;
49};
50```
51
52<details><summary>With props</summary>
53
54```js
55import { useResizeDetector } from 'react-resize-detector';
56
57const CustomComponent = () => {
58 const onResize = useCallback(() => {
59 // on resize logic
60 }, []);
61
62 const { width, height, ref } = useResizeDetector({
63 handleHeight: false,
64 refreshMode: 'debounce',
65 refreshRate: 1000,
66 onResize
67 });
68
69 return <div ref={ref}>{`${width}x${height}`}</div>;
70};
71```
72
73</details>
74
75<details><summary>With custom ref</summary>
76
77```js
78import { useResizeDetector } from 'react-resize-detector';
79
80const CustomComponent = () => {
81 const targetRef = useRef();
82 const { width, height } = useResizeDetector({ targetRef });
83 return <div ref={targetRef}>{`${width}x${height}`}</div>;
84};
85```
86
87</details>
88
89#### 2. HOC pattern
90
91```jsx
92import { withResizeDetector } from 'react-resize-detector';
93
94const CustomComponent = ({ width, height }) => <div>{`${width}x${height}`}</div>;
95
96export default withResizeDetector(CustomComponent);
97```
98
99#### 3. Child Function Pattern
100
101```jsx
102import ReactResizeDetector from 'react-resize-detector';
103
104// ...
105
106<ReactResizeDetector handleWidth handleHeight>
107 {({ width, height }) => <div>{`${width}x${height}`}</div>}
108</ReactResizeDetector>;
109```
110
111<details><summary>Full example (Class Component)</summary>
112
113```jsx
114import React, { Component } from 'react';
115import { withResizeDetector } from 'react-resize-detector';
116
117const containerStyles = {
118 height: '100vh',
119 display: 'flex',
120 alignItems: 'center',
121 justifyContent: 'center'
122};
123
124class AdaptiveComponent extends Component {
125 state = {
126 color: 'red'
127 };
128
129 componentDidUpdate(prevProps) {
130 const { width } = this.props;
131
132 if (width !== prevProps.width) {
133 this.setState({
134 color: width > 500 ? 'coral' : 'aqua'
135 });
136 }
137 }
138
139 render() {
140 const { width, height } = this.props;
141 const { color } = this.state;
142 return <div style={{ backgroundColor: color, ...containerStyles }}>{`${width}x${height}`}</div>;
143 }
144}
145
146const AdaptiveWithDetector = withResizeDetector(AdaptiveComponent);
147
148const App = () => {
149 return (
150 <div>
151 <p>The rectangle changes color based on its width</p>
152 <AdaptiveWithDetector />
153 </div>
154 );
155};
156
157export default App;
158```
159
160</details>
161
162<details><summary>Full example (Functional Component)</summary>
163
164```jsx
165import React, { useState, useEffect } from 'react';
166import { withResizeDetector } from 'react-resize-detector';
167
168const containerStyles = {
169 height: '100vh',
170 display: 'flex',
171 alignItems: 'center',
172 justifyContent: 'center'
173};
174
175const AdaptiveComponent = ({ width, height }) => {
176 const [color, setColor] = useState('red');
177
178 useEffect(() => {
179 setColor(width > 500 ? 'coral' : 'aqua');
180 }, [width]);
181
182 return <div style={{ backgroundColor: color, ...containerStyles }}>{`${width}x${height}`}</div>;
183};
184
185const AdaptiveWithDetector = withResizeDetector(AdaptiveComponent);
186
187const App = () => {
188 return (
189 <div>
190 <p>The rectangle changes color based on its width</p>
191 <AdaptiveWithDetector />
192 </div>
193 );
194};
195
196export default App;
197```
198
199</details>
200
201<br/>
202
203We still support [other ways](https://github.com/maslianok/react-resize-detector/tree/v4.2.1#examples) to work with this library, but in the future consider using the ones described above. Please let me know if the examples above don't fit your needs.
204
205## Performance optimization
206
207This library uses the native [ResizeObserver](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver) API.
208
209DOM nodes get attached to `ResizeObserver.observe` every time the component mounts and every time any property gets changed.
210
211It means you should try to avoid passing anonymous functions to `ResizeDetector`, because they will trigger the whole initialization process every time the component rerenders. Use `useCallback` whenever it's possible.
212
213```jsx
214// WRONG - anonymous function
215const { ref, width, height } = useResizeDetector({
216 onResize: () => {
217 // on resize logic
218 }
219});
220
221// CORRECT - `useCallback` approach
222const onResize = useCallback(() => {
223 // on resize logic
224}, []);
225
226const { ref, width, height } = useResizeDetector({ onResize });
227```
228
229## Refs
230
231_The below explanation doesn't apply to `useResizeDetector`_
232
233The library is trying to be smart and does not add any extra DOM elements to not break your layouts. That's why we use [`findDOMNode`](https://reactjs.org/docs/reactdom.html#finddomnode) method to find and attach listeners to the existing DOM elements. Unfortunately, this method has been deprecated and throws a warning in StrictMode.
234
235For those who want to avoid this warning, we are introducing an additional property - `targetRef`. You have to set this prop as a `ref` of your target DOM element and the library will use this reference instead of searching the DOM element with help of `findDOMNode`
236
237<details><summary>HOC pattern example</summary>
238
239```jsx
240import { withResizeDetector } from 'react-resize-detector';
241
242const CustomComponent = ({ width, height, targetRef }) => <div ref={targetRef}>{`${width}x${height}`}</div>;
243
244export default withResizeDetector(CustomComponent);
245```
246
247</details>
248
249<details><summary>Child Function Pattern example</summary>
250
251```jsx
252import ReactResizeDetector from 'react-resize-detector';
253
254// ...
255
256<ReactResizeDetector handleWidth handleHeight>
257 {({ width, height, targetRef }) => <div ref={targetRef}>{`${width}x${height}`}</div>}
258</ReactResizeDetector>;
259```
260
261</details>
262
263## API
264
265| Prop | Type | Description | Default |
266| --------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- |
267| onResize | Func | Function that will be invoked with `width` and `height` arguments | `undefined` |
268| handleWidth | Bool | Trigger `onResize` on width change | `true` |
269| handleHeight | Bool | Trigger `onResize` on height change | `true` |
270| skipOnMount | Bool | Do not trigger onResize when a component mounts | `false` |
271| refreshMode | String | Possible values: `throttle` and `debounce` See [lodash docs](https://lodash.com/docs#debounce) for more information. `undefined` - callback will be fired for every frame | `undefined` |
272| refreshRate | Number | Use this in conjunction with `refreshMode`. Important! It's a numeric prop so set it accordingly, e.g. `refreshRate={500}` | `1000` |
273| refreshOptions | Object | Use this in conjunction with `refreshMode`. An object in shape of `{ leading: bool, trailing: bool }`. Please refer to [lodash's docs](https://lodash.com/docs/4.17.11#throttle) for more info | `undefined` |
274| observerOptions | Object | These options will be used as a second parameter of [`resizeObserver.observe`](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver/observe) method. | `undefined` |
275| targetRef | Ref | Use this prop to pass a reference to the element you want to attach resize handlers to. It must be an instance of `React.useRef` or `React.createRef` functions | `undefined` |
276
277## Testing with Enzyme and Jest
278
279Thanks to [@Primajin](https://github.com/Primajin) for posting this [snippet](https://github.com/maslianok/react-resize-detector/issues/145)
280
281```jsx
282const { ResizeObserver } = window;
283
284beforeEach(() => {
285 delete window.ResizeObserver;
286 window.ResizeObserver = jest.fn().mockImplementation(() => ({
287 observe: jest.fn(),
288 unobserve: jest.fn(),
289 disconnect: jest.fn()
290 }));
291
292 wrapper = mount(<MyComponent />);
293});
294
295afterEach(() => {
296 window.ResizeObserver = ResizeObserver;
297 jest.restoreAllMocks();
298});
299
300it('should do my test', () => {
301 // [...]
302});
303```
304
305## License
306
307MIT
308
309## ❤️
310
311Show us some love and STAR ⭐ the project if you find it useful