Skip to content

Instantly share code, notes, and snippets.

@paulhhowells
Created May 31, 2020 19:10
Show Gist options
  • Save paulhhowells/9f36676bddfd5fbf0979e56c659e58bd to your computer and use it in GitHub Desktop.
Save paulhhowells/9f36676bddfd5fbf0979e56c659e58bd to your computer and use it in GitHub Desktop.
import React from 'react';
import useBreakpointWidth from '../../hooks/useBreakpointWidth';
import './index.css';
const breakpointWidths = [
{ width: 200, classNames: 'component-narrow' },
{ width: 300, classNames: 'w-300' },
{ width: 400, classNames: 'w-400' },
{ width: 600, classNames: 'w-600' },
{ width: 800, classNames: 'component--wide' },
];
function ResizeTest () {
const { observedElementRef:ref, matchedBreakpointWidth, classNames } = useBreakpointWidth(breakpointWidths);
const className = 'resize-test' + (classNames
? ` ${classNames}`
: '');
return (
<div ref={ ref } className={ className }>
<h2>Resize Test</h2>
<pre>{ matchedBreakpointWidth }</pre>
{ (matchedBreakpointWidth === 400)
? <div>four hundred mark-up</div>
: <div>other mark-up</div>
}
</div>
);
}
export default ResizeTest;
<!---------------------------------->
import {
useLayoutEffect, // fires synchronously after all DOM mutations
useRef,
useState,
} from 'react';
export default useBreakpointWidth;
/**
*
* @param {Array} breakpointWidths
* breakpointWidths should be in the format:
* [{ width: 400, classNames: 'narrow' }, { width: 1000, classNames: 'wide' }]
*/
function useBreakpointWidth (breakpointWidths) {
const [ matchedBreakpoint, setMatchedBreakpoint ] = useState({});
// Create a ref for the DOM element that will be observed.
const observedElementRef = useRef(null);
// Sort breakpoint widths into ascending order.
const breakpoints = [ ...breakpointWidths ].sort((a, b) => a.width - b.width);
useLayoutEffect(() => {
if (ResizeObserver && observedElementRef) {
const observedElement = observedElementRef.current;
const resizeObserver = new ResizeObserver(resizeCallback);
resizeObserver.observe(observedElement);
// return () => resizeObserver.unobserve(observedElement);
return () => resizeObserver.disconnect();
}
function resizeCallback (entries) {
const entry = entries[0]; // Assume only one entry.
const width = (entry.contentBoxSize)
? entry.contentBoxSize.inlineSize // Newer ResizeObserverEntry property.
: entry.contentRect.width; // Older ResizeObserverEntry property.
setMatchedBreakpoint(getWidestBreakpoint(width, breakpoints));
}
}, [ observedElementRef, breakpoints ]);
const matchedBreakpointWidth = matchedBreakpoint && matchedBreakpoint.width || false;
const classNames = matchedBreakpoint && matchedBreakpoint.classNames || '';
return {
observedElementRef,
matchedBreakpointWidth,
classNames,
};
}
function getWidestBreakpoint (width, breakpoints) {
// breakpoints.forEach(breakpoint => {
// if (width >= breakpoint.width) {
// return breakpoint;
// }
// });
// Iterate through the breakpoint widths starting with the widest.
// Dependent on breakpoint widths being listed in ascending order
// to support a Mobile First approach.
for (let i = breakpoints.length - 1; i >= 0; i--) {
const breakpoint = breakpoints[i];
if (width >= breakpoint.width) {
return breakpoint;
}
}
return false;
}
<!---------------------------------->
import {
useLayoutEffect, // fires synchronously after all DOM mutations
useRef,
useState,
} from 'react';
export default useBreakpointWidth;
/**
*
* @param {Array} breakpointWidths
* breakpointWidths should be in the format:
* [{ width: 400, classNames: 'narrow' }, { width: 1000, classNames: 'wide' }]
*/
function useBreakpointWidth (breakpointWidths) {
const [ classNames, setClassNames ] = useState('');
// Create a ref for the DOM element that will be observed.
const observedElementRef = useRef(null);
// Sort breakpoint widths into ascending order.
const breakpoints = [ ...breakpointWidths ].sort((a, b) => a.width - b.width);
useLayoutEffect(() => {
if (ResizeObserver) {
const observedElement = observedElementRef.current;
const resizeObserver = new ResizeObserver(resizeCallback);
resizeObserver.observe(observedElement);
return () => resizeObserver.disconnect();
}
function resizeCallback (entries) {
const entry = entries[0]; // Assume only one entry.
const width = (entry.contentBoxSize)
? entry.contentBoxSize.inlineSize // newer ResizeObserverEntry property
: entry.contentRect.width; // older ResizeObserverEntry property
const classNames = getWidestBreakpoint(width, breakpoints).classNames || '';
setClassNames(classNames);
}
}, [ observedElementRef, breakpoints ]);
return [ observedElementRef, classNames ];
}
function getWidestBreakpoint (width, breakpoints) {
// Dependent on breakpoint widths being listed in ascending order
// to support a Mobile First approach.
for (let i = breakpoints.length - 1; i >= 0; i--) {
const breakpoint = breakpoints[i];
if (width >= breakpoint.width) {
return breakpoint;
}
}
return false;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment