Skip to content

Instantly share code, notes, and snippets.

@pigoz
Last active February 26, 2019 18:51
Show Gist options
  • Save pigoz/eadfc231008193ff8bdbacedeff2b7d5 to your computer and use it in GitHub Desktop.
Save pigoz/eadfc231008193ff8bdbacedeff2b7d5 to your computer and use it in GitHub Desktop.
useMedia implementation with React Hooks and TypeScript
import React, { useState, useEffect, useContext } from "react";
type MediaQueries = { [s: string]: MediaQueryList };
type MediaT<T> = { [X in keyof T]: boolean };
function mapValues<T extends object, R>(
object: T,
mapper: (x: T[keyof T], key: keyof T, object: T) => R
): { [K in keyof T]: R } {
const result = {};
Object.keys(object).forEach(key => {
// @ts-ignore
result[key] = mapper(object[key], key, object);
});
// @ts-ignore
return result;
}
export function createMediaContext<T extends MediaQueries>(media: T) {
const empty = mapValues(media, () => false);
const Context = React.createContext<MediaT<T>>(empty);
function MediaProvider(props: { children: JSX.Element }) {
const [state, setState] = useState<MediaT<T>>(
mapValues(media, v => v.matches)
);
useEffect(() => {
const xs = Object.keys(media).map(k => {
const mql = media[k];
function listener(ev: { matches: boolean }) {
setState(Object.assign({}, state, { [k]: ev.matches }));
}
listener.bind(mql);
mql.addListener(listener);
return { mql, listener };
});
return () => {
xs.forEach(x => {
x.mql.removeListener(
// @ts-ignore
x.listener
);
});
};
});
return <Context.Provider value={state}>{props.children}</Context.Provider>;
}
return { Provider: MediaProvider, Consumer: Context.Consumer, Context };
}
const BootstrapContext = createMediaContext({
xs: window.matchMedia("(min-width: 0px)"),
sm: window.matchMedia("(min-width: 768px)"),
md: window.matchMedia("(min-width: 992px)"),
lg: window.matchMedia("(min-width: 1200px)")
});
// Provider that manages media state
export const Provider = BootstrapContext.Provider;
// Render-props based API
export const Media = BootstrapContext.Consumer;
// React Hook
export const useMedia = () => useContext(BootstrapContext.Context);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment