Skip to content

Instantly share code, notes, and snippets.

@nicolas-zozol
Created May 17, 2023 07:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nicolas-zozol/4495dae3c0b7531b4a13cd2c2471a96f to your computer and use it in GitHub Desktop.
Save nicolas-zozol/4495dae3c0b7531b4a13cd2c2471a96f to your computer and use it in GitHub Desktop.
useSize: get responsives sizes inside TSX code with React
/**
* This custom hook is used to get the size of the screen and creates
* a context to share the size with all the components.
*/
import React, { FC, useEffect, useState } from 'react'
export const tabletSize = '634px'
export const desktopSize = '1206px'
const tabletOrSmallerMediaQuery = `(max-width: ${desktopSize})`
const mobileMediaQuery = `(max-width: ${tabletSize})`
const desktopMediaQuery = `(min-width: ${desktopSize})`
export function isTabletOrSmaller() {
return window.matchMedia(`(max-width: ${desktopSize})`).matches
}
export function isMobile() {
return window.matchMedia(`(max-width: ${tabletSize})`).matches
}
export function isDesktop() {
return window.matchMedia(`(min-width: ${desktopSize})`).matches
}
function debounce<F extends (...params: any[]) => void>(fn: F, delay: number) {
let timeoutID: number = 0
return function (this: any, ...args: any[]) {
clearTimeout(timeoutID)
timeoutID = window.setTimeout(() => fn.apply(this, args), delay)
} as F
}
function setter(): ISizeContext {
return {
isTabletOrSmaller: isTabletOrSmaller(),
isMobile: isMobile(),
isDesktop: isDesktop(),
}
}
interface ISizeContext {
isTabletOrSmaller: boolean
isMobile: boolean
isDesktop: boolean
}
const initialState: ISizeContext = setter()
export const SizeContext = React.createContext<ISizeContext>(initialState)
export const SizeProvider: FC = ({ children }) => {
const [screenSize, setMyScreenSize] = useState<ISizeContext>(initialState)
const tabletOrSmallerMediaQueryMatcher = window.matchMedia(
tabletOrSmallerMediaQuery
)
const mobileMediaQueryMatcher = window.matchMedia(mobileMediaQuery)
const desktopMediaQueryMatcher = window.matchMedia(desktopMediaQuery)
const handler = debounce(function onChange() {
setMyScreenSize(setter())
}, 300)
useEffect(() => {
tabletOrSmallerMediaQueryMatcher.addEventListener('change', handler)
mobileMediaQueryMatcher.addEventListener('change', handler)
desktopMediaQueryMatcher.addEventListener('change', handler)
// Remove listeners on cleanup
return () => {
tabletOrSmallerMediaQueryMatcher.removeEventListener('change', handler)
mobileMediaQueryMatcher.removeEventListener('change', handler)
desktopMediaQueryMatcher.removeEventListener('change', handler)
}
}, [])
return (
<SizeContext.Provider
value={{
isTabletOrSmaller: screenSize.isTabletOrSmaller,
isMobile: screenSize.isMobile,
isDesktop: screenSize.isDesktop,
}}
>
{children}
</SizeContext.Provider>
)
}
export const useSize = () => React.useContext(SizeContext)
@nicolas-zozol
Copy link
Author

nicolas-zozol commented May 17, 2023

After adding the SizeContext at the right place, you can use it like that:

export const Header: FC<Props> = () => {
  const { isDesktop } = useSize()
  return isDesktop ? (    
    <Line onThemeToggle={...}>....</Line>
  ) : (
    <MobileView onThemeToggle={...} />
  )
}

It's useful to mix with other logic variables:

const { isMobile} = useSize()
<Content>
  {connected && !isMobile && <HashImage />}
</Content>

Have fun :)

@nicolas-zozol
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment