Skip to content

Instantly share code, notes, and snippets.

@moritzsalla
Last active January 7, 2023 20:35
Show Gist options
  • Save moritzsalla/ba642f83fb3f16493014af58ed6aa5b0 to your computer and use it in GitHub Desktop.
Save moritzsalla/ba642f83fb3f16493014af58ed6aa5b0 to your computer and use it in GitHub Desktop.
Some react tricks
// fallback for a partially supported hook
// source https://github.com/tailwindlabs/headlessui/blob/main/packages/%40headlessui-react/src/hooks/use-id.ts
import React from 'react'
import { useIsoMorphicEffect } from './use-iso-morphic-effect'
import { useServerHandoffComplete } from './use-server-handoff-complete'
import { env } from '../utils/env'
export let useId =
// Prefer React's `useId` if it's available.
// @ts-expect-error - `useId` doesn't exist in React < 18.
React.useId ??
function useId() {
let ready = useServerHandoffComplete()
let [id, setId] = React.useState(ready ? () => env.nextId() : null)
useIsoMorphicEffect(() => {
if (id === null) setId(env.nextId())
}, [id])
return id != null ? '' + id : undefined
}
// show a progress bar on `next/router` progress
// source https://github.com/gaearon/whatthefuck.is/blob/main/pages/_app.js
import React from 'react'
import Router from 'next/router'
import App from 'next/app'
import nprogress from 'nprogress'
import debounce from 'lodash.debounce'
//! Only show nprogress after 500ms (slow loading)
const start = debounce(nprogress.start, 500)
Router.events.on('routeChangeStart', start)
Router.events.on('routeChangeComplete', url => {
start.cancel()
nprogress.done()
window.scrollTo(0, 0)
})
Router.events.on('routeChangeError', () => {
start.cancel()
nprogress.done()
})
// Safer context - enforces users to wrap their context getters in context provider, * and throws more helpful errors for debugging.
const LabelContext = createContext(null)
const useLabelContext = () => {
let context = useContext(LabelContext)
if (context === null) {
const err = new ReferenceError('You used a <${Label.displayName} /> component, but it is not inside a relevant parent.')
if (Error.captureStackTrace) Error.captureStackTrace(err, useLabelContext)
throw err
}
return context
}
// user forget's to implement the context provider:
/**
* @example
* const [labelledby, LabelProvider] = useLabel()
* return (
* <LabelProvider>
* <div aria-labelledby={labelledby}>{...}</div>
* </LabelProvider>
* )
*/
const useLabel = ({ children }) => {
const labelId = useId()
return [
labelId,
useMemo(() => {
const LabelProvider = () => {
const contextBag = useMemo(
() => ({ ... }),
[...]
)
return <LabelContext.Provider value={contextBag}>{children}</LabelContext.Provider>
}
return LabelProvider
}, [labelId])
]
}
const Label = () => {
const context = useLabelContext()
// ReferenceError: 'You used a <${Label.displayName} /> component, but it is not inside a relevant parent.'
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment