Skip to content

Instantly share code, notes, and snippets.

@pigoz
Last active February 26, 2019 18:51

Revisions

  1. pigoz revised this gist Nov 25, 2018. 1 changed file with 9 additions and 3 deletions.
    12 changes: 9 additions & 3 deletions useMedia.tsx
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,7 @@
    import React, { useState, useEffect, useContext } from "react";

    type MediaQueries = { [s: string]: MediaQueryList };
    type Media<T> = { [X in keyof T]: boolean };
    type MediaT<T> = { [X in keyof T]: boolean };

    function mapValues<T extends object, R>(
    object: T,
    @@ -18,10 +18,10 @@ function mapValues<T extends object, R>(

    export function createMediaContext<T extends MediaQueries>(media: T) {
    const empty = mapValues(media, () => false);
    const Context = React.createContext<Media<T>>(empty);
    const Context = React.createContext<MediaT<T>>(empty);

    function MediaProvider(props: { children: JSX.Element }) {
    const [state, setState] = useState<Media<T>>(
    const [state, setState] = useState<MediaT<T>>(
    mapValues(media, v => v.matches)
    );

    @@ -62,5 +62,11 @@ const BootstrapContext = createMediaContext({
    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);
  2. pigoz revised this gist Nov 25, 2018. No changes.
  3. pigoz revised this gist Nov 25, 2018. 1 changed file with 4 additions and 1 deletion.
    5 changes: 4 additions & 1 deletion useMedia.tsx
    Original file line number Diff line number Diff line change
    @@ -41,7 +41,10 @@ export function createMediaContext<T extends MediaQueries>(media: T) {

    return () => {
    xs.forEach(x => {
    x.mql.removeListener(x.listener as any);
    x.mql.removeListener(
    // @ts-ignore
    x.listener
    );
    });
    };
    });
  4. pigoz created this gist Nov 25, 2018.
    63 changes: 63 additions & 0 deletions useMedia.tsx
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,63 @@
    import React, { useState, useEffect, useContext } from "react";

    type MediaQueries = { [s: string]: MediaQueryList };
    type Media<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<Media<T>>(empty);

    function MediaProvider(props: { children: JSX.Element }) {
    const [state, setState] = useState<Media<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(x.listener as any);
    });
    };
    });

    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)")
    });

    export const Provider = BootstrapContext.Provider;
    export const useMedia = () => useContext(BootstrapContext.Context);