Skip to content

Instantly share code, notes, and snippets.

@adam-zethraeus
Last active July 25, 2023 14:21
Show Gist options
  • Save adam-zethraeus/b011617e7e2228d56c89d4896d4e9378 to your computer and use it in GitHub Desktop.
Save adam-zethraeus/b011617e7e2228d56c89d4896d4e9378 to your computer and use it in GitHub Desktop.
media-query based conditional React components

React Breakpoints

media-query based conditional React component rendering

Usage:

import { Show } from "./Breakpoints"

const sm = 640;
const lg = 1024;

const MyComponent = (props) => {
  <Show upto={sm}>
    rendered when viewport is smaller than 640px
  </Show>
  <Show from={sm} upto={lg}>
    rendered when viewport is both:
        - at least 640px
        - smaller than 1024px
  </Show>
  <Show from={lg}>
    rendered when viewport is at least 1024px
  </Show>
}

Alternatively:

import { useMinWidthBreakpoint } from "./Breakpoints"

const MyComponent = (props) => {
  const greaterThanOrEqualTo1024 = useMinWidthBreakpoint(1024);
  if (greaterThanOrEqualTo1024) {
    return (<h1>Real Big</h1>);
  } else {
    return (<p>Kinda Small</p>);
  }
};
import { useState, useEffect } from "react";
const makeObservable = (target) => {
let listeners = [];
let value = target;
const getter = () => {
return value;
};
const count = () => {
return listeners.length;
};
const setter = (newValue) => {
if (value === newValue) return;
value = newValue;
listeners.forEach((l) => l(value));
};
const subscribe = (listenerFunc) => {
listeners.push(listenerFunc);
return () => unsubscribe(listenerFunc);
};
const unsubscribe = (listenerFunc) => {
listeners = listeners.filter((l) => l !== listenerFunc);
};
return {
count,
getter,
setter,
subscribe,
};
};
const INVALID = -1;
const ZERO = 0;
const INF = 9007199254740991;
const defaults = {
"-1": { obs: { getter: () => false }, mq: null, el: null },
0: { obs: { getter: () => true }, mq: null, el: null },
9007199254740991: { obs: { getter: () => false }, mq: null, el: null },
};
const isDefault = (x) => {
return !!defaults[x];
};
let subscriptions = {};
const useMinWidthBreakpoint = (breakpoint) => {
const bp = Number(breakpoint) ?? INVALID;
let tuple = defaults[bp] || subscriptions[bp];
if (!tuple) {
const mq = window.matchMedia(`(min-width: ${bp}px)`);
const obs = makeObservable(mq.matches);
const el = mq.addEventListener("change", (e) => obs.setter(e.matches));
tuple = { obs, mq, el };
subscriptions[bp] = tuple;
}
const { obs, mq, el } = tuple;
const [value, setValue] = useState(obs.getter());
useEffect(() => {
if (isDefault(bp)) {
return;
}
const unsub = obs.subscribe(setValue);
return () => {
unsub();
if (obs.count() < 1 && !isDefault(bp)) {
mq.removeEventListener("change", el);
subscriptions[bp] = null;
}
};
}, [bp, obs, mq, el]);
return value;
};
const Show = ({ children, from, upto }) => {
const checkedFrom = Number.isInteger(from) ? from : ZERO;
const checkedUpTo = Number.isInteger(upto) ? upto : INF;
const fromBP = useMinWidthBreakpoint(checkedFrom);
const uptoBP = useMinWidthBreakpoint(checkedUpTo);
if (fromBP && !uptoBP) {
return children;
} else {
return <></>;
}
};
export { useMinWidthBreakpoint, Show };
const Bootstrap = {
`xs`: 0,
`sm`: 576,
`md`: 768,
`lg`: 992,
`xl`: 1200,
`xxl`: 1400,
};
const Tailwind = {
`sm`: 640,
`md`: 768,
`lg`: 1024,
`xl`: 1280,
`2xl`: 1536,
};
export { Bootstrap, Tailwind };
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment