Skip to content

Commit 20ed4ed

Browse files
authored
Merge pull request #2 from rob2d/features/support-ssr
Support SSR; closes issue #1
2 parents 7e3e453 + 7229ded commit 20ed4ed

File tree

4 files changed

+57
-18
lines changed

4 files changed

+57
-18
lines changed

README.md

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@ npm install -D use-viewport-sizes
99
```
1010

1111
## Benefits
12-
- extremely lightweight and dependency-free; **2.54kb pre-gzipped** with all dependencies.
12+
- extremely lightweight and dependency-free; **2.59kb pre-gzipped** with all dependencies.
1313
- only one `window.onresize` handler used to subscribe to any changes in an unlimited number of components.
1414
- optional debounce to delay updates until user stops dragging their window for a moment; this can make expensive components with size-dependent calculations run much faster and your app feel smoother.
1515
- debouncing does not create new handlers or waste re-renders in your component; the results are also pooled from only one resize result.
16+
- supports SSR.
1617

1718
## Usage ##
1819

@@ -24,6 +25,7 @@ npm install -D use-viewport-sizes
2425
*registers dimension changes on every resize event immediately*
2526

2627
```
28+
import React from 'react'
2729
import useViewportSizes from 'use-viewport-sizes'
2830
2931
function MyComponent (props) {
@@ -38,6 +40,7 @@ function MyComponent (props) {
3840
*registers dimension changes only when a user stops dragging/resizing the window for a specified number of miliseconds; for expensive components such as data grids which may be too
3941
expensive to re-render during window resize dragging.*
4042
```
43+
import React from 'react'
4144
import useViewportSizes from 'use-viewport-sizes'
4245
4346
function MyExpensivelyRenderedComponent (props) {
@@ -47,3 +50,25 @@ function MyExpensivelyRenderedComponent (props) {
4750
}
4851
```
4952

53+
### **Server Side Rendering**
54+
55+
*While serverside rendering is supported, it requires an explicit update via `useEffect` since viewport does not actually exist on the server before rendering to client. The following has been tested with [NextJS](https://nextjs.org/).*
56+
57+
*Sidenote that you will see a `useLayoutEffect` warning from React. This is perfectly normal as there is no viewport/context to paint to when pre-rendering in SSR and will not interfere with your app once served to the client*
58+
59+
```
60+
import React, { useLayoutEffect } from 'react'
61+
import useViewportSizes from 'use-viewport-sizes'
62+
63+
function MySSRComponent (props) {
64+
const [vpWidth, vpHeight, updateVpSizes] = useViewportSizes()
65+
66+
// below, we add one post-render update
67+
// in order to register the client's viewport sizes
68+
// after serving SSR content
69+
70+
useEffect(()=> { updateVpSizes(); }, []);
71+
72+
...renderLogic
73+
}
74+
```

build/index.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "use-viewport-sizes",
3-
"version": "0.1.10",
3+
"version": "0.2.0",
44
"description": "tiny React hook which allows you to track visible window viewport size in your components w/ an optional debounce for updates for optimal rendering.",
55
"main": "build/index.js",
66
"scripts": {

src/index.js

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,48 +6,62 @@ import { useState, useEffect, useMemo } from "react"
66

77
var { documentElement } = window.document;
88

9-
let something;
10-
119
function getVpWidth () {
12-
return Math.max(
10+
return (typeof window != 'undefined') ? Math.max(
1311
documentElement.clientWidth,
1412
window.innerWidth || 0
15-
);
13+
) : 0;
1614
}
1715

1816

1917
function getVpHeight () {
20-
return Math.max(
18+
return (typeof window != 'undefined') ? Math.max(
2119
documentElement.clientHeight,
2220
window.innerHeight || 0
23-
);
21+
) : 0;
2422
}
2523

2624
// =============== //
2725
// Shared State //
2826
// =============== //
2927

28+
// using separate variables since Babel
29+
// transpilation saves a bit of filesize
30+
3031
var listeners = new Set();
31-
var vpW = getVpWidth();
32-
var vpH = getVpHeight();
32+
var vpW = 0;
33+
var vpH = 0;
34+
35+
let hasListenerBeenAttached = false;
36+
3337

34-
function onResize(vpWidth, vpHeight) {
38+
// should only be called by *one* component once;
39+
// will iterate through all subscribers
40+
// afterwards
41+
42+
function onResize() {
43+
let vpWidth = getVpWidth();
44+
let vpHeight = getVpHeight();
3545
listeners.forEach(function(listener) {
3646
listener({ vpWidth, vpHeight });
3747
});
3848
}
3949

40-
window.addEventListener('resize',function(){
41-
vpW = getVpWidth();
42-
vpH = getVpHeight();
43-
onResize(vpW, vpH);
44-
});
50+
4551

4652
// =============== //
4753
// the Hook //
4854
// =============== //
4955

5056
function useViewportSizes(debounce) {
57+
useLayoutEffect(()=> {
58+
if(window && !hasListenerBeenAttached) {
59+
hasListenerBeenAttached = true;
60+
window.addEventListener('resize', onResize);
61+
onResize();
62+
}
63+
}, []);
64+
5165
const [{ vpWidth, vpHeight }, setState] = useState(() => ({
5266
vpWidth : vpW, vpHeight : vpH
5367
}));
@@ -75,7 +89,7 @@ function useViewportSizes(debounce) {
7589
return () => listeners.delete(listener);
7690
}, []);
7791

78-
return [vpWidth, vpHeight];
92+
return [vpWidth, vpHeight, onResize];
7993
}
8094

8195
export default useViewportSizes

0 commit comments

Comments
 (0)