npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@rs1/react-hooks

v1.1.18

Published

A collection of useful custom React hooks by RS1

Downloads

7

Readme

ReactHooks

A collection of useful custom React hooks.

To install this package run:

npm i -D @rs1/react-hooks

Here you can view a working example of every hook, with walk through sample code:
Demo site

Available hooks

  • useAgentParser
    Use it to parse the current user-agent string and to detect if the device is touch enabled.

  • useAutoIdle
    Use it to update your UI when the user isn't interacting with a component.

  • useDynamicRef
    Use it to keep a dynamic – auto-updating or manual-updating – reference to a DOM node.

  • useFullScreen
    Use it to gain access to the native Full Screen API.

  • useKeyAction
    Use it to link a callback to a key press, updating it when any dependencies change.

  • useKeyLinkedRef
    Use it to easily let a keyboard key press simulate the click on a DOM node.

  • useListener
    Use it to attach an event listener to a DOM node, updating it when the DOM node or any dependencies change.

  • useMediaQuery
    Use it to update your UI based on the viewport size of the browser.

  • useRectRef
    Use it to keep a reference to the bounding client rect of a DOM node.

  • useRefEffect
    Use it in combination with useDynamicRef to react to a DOM node change.

  • useSliderRef
    Use it to transform any DOM node in a horizontal slider, handling touch and drag events

useAgentParser

Useful to parse the current user-agent string and to detect if the device is touch enabled.

How to use it in your React component:

import React from 'react'
import { useAgentParser } from '@rs1/react-hooks'

export default (props) => {
    const {
        isTouch,
        OS: {
            name: osName,
            version: osVersion,
        },
        browser: {
            name: browserName,
            version: browserVersion,
            major: browserMajor,
        },
        agent,
    } = useAgentParser()

    return (
        <div>
            <h3>{`${osName} (v${osVersion})`}</h3>
            {`You're ${isTouch ? 'touching' : 'surfing'} the web
            with ${browserName} ${browserMajor} (v${browserVersion}),
            what a wonderful browser!`}
            <hr />
            <pre>{agent}</pre>
        </div>
    )
}

useAutoIdle

Useful if you need to update your UI when the user isn't interacting with a component.

How to use it in your React component:

export default props => {
    const node = useRef()
    const [isIdle] = useAutoIdle(
        // Which node should be watched?
        node?.current,
        {
            // What callback should be fired on change?
            callback: idle =>
                console.log(
                    `The watched node is now ${idle ? 'idle' : 'active'}`
                ),
            // How many seconds shoud be waited before going to idle?
            wait: 5,
            // Is there a condition that should be true before going to idle?
            condition: true,
            // Is the node initially idle?
            initial: false,
        }
    )

    return (
        <div
            ref={node}
            style={{
                minWidth: '300px',
                minHeight: '300px',
                background: isIdle ? 'red' : 'blue',
                color: '#ffffff',
                padding: '15px',
            }}
        >
            This node reacts to your mouse movements. After 5 seconds that you
            stopped interacting, it'll go idle.
            <hr />
            {`This node is currently ${isIdle ? 'idle' : 'active'}`}
        </div>
    )
}

useDynamicRef

Useful to keep a dynamic – auto-updating or manual-updating – reference to a DOM node.

How to use it in your React component:

import React, { useEffect, useRef } from 'react'
import { useDynamicRef } from '@rs1/react-hooks'

export default ({ url, isVideo, ...props }) => {
    const [media, mediaRef /* , updateMediaRef */] = useDynamicRef(
        /* Initial value: */ null,
        /* Require manual-update: */ false
    )
    useEffect(() => {
        console.log(`The media ref tag is: ${media?.tagName}`)
    }, [media])

    /* Comparison with the standard useRef hook */
    const stdRef = useRef()
    useEffect(() => {
        console.log(`The standard ref tag is: ${media?.tagName}`)
    }, [stdRef])

    return (
        <div>
            <h6>
                useDynamicRef - Current tag: {media?.tagName || 'undefined'}
            </h6>
            {isVideo ? (
                <video
                    ref={mediaRef}
                    src={url}
                    controls
                    style={{ width: '100%' }}
                />
            ) : (
                <audio
                    ref={mediaRef}
                    src={url}
                    controls
                    style={{ width: '100%' }}
                />
            )}
            <hr />
            <h6>useRef - Current tag: {stdRef?.tagName || 'undefined'}</h6>
            {isVideo ? (
                <video
                    ref={stdRef}
                    src={url}
                    controls
                    style={{ width: '100%' }}
                />
            ) : (
                <audio
                    ref={stdRef}
                    src={url}
                    controls
                    style={{ width: '100%' }}
                />
            )}
        </div>
    )
}

useFullScreen

Useful to gain access to the native Full Screen API.

How to use it in your React component:

import React, { useRef } from 'react'
import { useFullScreen } from '@rs1/react-hooks'

export default props => {
    const containerRef = useRef()
    const videoRef = useRef()
    const {
        // request,
        // exit,
        toggle,
        isActive,
        isSupported,
    } = useFullScreen({
        onChange: () => console.log(`Full screen status changed`),
        onError: () => console.log(`Full screen request failed`),
    })

    const bbbMovie =
        'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4'

    return (
        <div>
            <div
                ref={containerRef}
                style={{
                    width: '100%',
                    height: '100%',
                    minHeight: '200px',
                    position: 'relative',
                    background: isActive ? '#000000' : '#ffffff',
                }}
            >
                <video
                    ref={videoRef}
                    style={{ width: '100%', height: '100%' }}
                    controls
                >
                    <source src={bbbMovie} type='video/mp4' />
                </video>
                {isSupported(containerRef, videoRef) && (
                    <button
                        onClick={() => {
                            /* We can feed two DOM nodes to the API,
                            the first is the one we want to be full screen,
                            the second is an optional media element to be set
                            full screen on the WebKit mobile version that doesn't
                            support the full screen API for normal DOM nodes. */
                            toggle(containerRef, videoRef)
                            /* This is the same as:
                            if (isActive)
                                exit()
                            else
                                request(containerRef, videoRef) */
                        }}
                        style={{
                            position: 'absolute',
                            top: '25px',
                            right: '25px',
                        }}
                    >{`${isActive ? 'Exit' : 'Enter'} full screen`}</button>
                )}
            </div>
        </div>
    )
}

useKeyAction

Useful to link a callback to a key press, updating it when any dependencies change.

How to use it in your React component:

import React, { useCallback } from 'react'
import { useKeyAction } from '@rs1/react-hooks'

export default (props) => {
    const callback = () => alert(`'K' key pressed!`)
    
    useKeyAction(
        /* 75 is the code for the 'K' key */
        75,

        /* The callback to fire when the key is pressed */
        callback,

        /* Optional array: dependencies */
        [],

        /* Optional boolean: whether or not should listen
         * to key press events coming from input elements
         * (input, button, select, textarea) */
        false
    )

    return (
        <div>
            <h5>Press 'K' and you'll see!</h5>
        </div>
    )
}

useKeyLinkedRef

Useful to easily let a keyboard key press simulate the click on a DOM node.

How to use it in your React component:

import React, { useCallback } from 'react'
import { useKeyLinkedRef } from '@rs1/react-hooks'

export default (props) => {
    /* 75 is the code for the 'K' key */
    const buttonRef = useKeyLinkedRef(75)

    const handleClick = useCallback(e => {
        console.log(`Received click on: "${e?.currentTarget?.id}"`)
    }, [])

    return (
        <div>
            <button ref={buttonRef} onClick={handleClick} id="key-linked-button">
                Click me or press 'K'
            </button>
            <h5>...and keep an eye on the console!</h5>
        </div>
    )
}

useListener

Useful to attach an event listener to a DOM node, updating it when the DOM node or any dependencies change.

How to use it in your React component:

import React, { useState, useCallback, useRef } from 'react'
import { useListener } from '@rs1/react-hooks'

export default props => {
    const sectionRef = useRef()
    const [counter, setCounter] = useState(0)

    useListener(
        sectionRef?.current,
        'click',
        () => console.log(`The counter is: ${counter}`),
        [counter]
    )

    const handleClick = useCallback(() => {
        setCounter(counter => counter + 1)
    }, [])

    return (
        <div>
            <p>Click the red section and check the console!</p>
            <button onClick={handleClick}>Increase counter ({counter})</button>
            <hr />
            <div
                ref={sectionRef}
                style={{ padding: '15px', background: 'red', color: '#ffffff' }}
            >
                I've used the useListener hook.
            </div>
        </div>
    )
}

useMediaQuery

Useful to update your UI based on the viewport size of the browser.

How to use it in your React component:

import React from 'react'
import { useMediaQuery } from '@rs1/react-hooks'

export default props => {
    /*
     * You can pass an array of breakpoint widths
     * to this custom hook, it'll return an array
     * of boolean values indicating if that size
     * is supported. Additionally an extra parameter
     * is returned to signal if no width is supported.
     * The widths must be passed in descending order.
     */
    const [
        desktop,
        laptop,
        tablet,
        smartphone,
        none
    ] = useMediaQuery([1920, 1024, 768, 414])

    /*
     * Alternatively, you can pass an object of
     * breakpoint widths for every key to this custom
     * hook, it'll return an object of boolean values
     * for every key indicating if that size is
     * supported. Additionally an extra key 'none'
     * is returned to signal if no width is supported.
     * The widths must be passed in descending order.
     */
    const mq = useMediaQuery({
        desktop: 1920,
        laptop: 1024,
        tablet: 768,
        smartphone: 414,
    })

    /*
     * The above 'useMediaQuery' hooks transform the
     * breakpoint widths to these conditions:
     * desktop:    min-width == 1920px
     * laptop:     min-width == 1024px && max-width == 1919px
     * tablet:     min-width ==  768px && max-width == 1023px
     * smartphone: min-width ==  414px && max-width ==  767px
     * none:       max-width ==  413px
     */

    return (
        <div>
            {desktop && <p>I'm visible on desktops</p>}
            {laptop && <p>I'm visible on laptops</p>}
            {tablet && <p>I'm visible on tablets</p>}
            {smartphone && <p>I'm visible on smartphones</p>}
            {none && <p>This screen size is not managed :(</p>}
            <hr />
            {mq.desktop && <p>I'm visible on desktops too</p>}
            {mq.laptop && <p>I'm visible on laptops too</p>}
            {mq.tablet && <p>I'm visible on tablets too</p>}
            {mq.smartphone && <p>I'm visible on smartphones too</p>}
            {mq.none && <p>This screen size is not managed here too :(</p>}
        </div>
    )
}

useRectRef

Useful to keep a reference to the bounding client rect of a DOM node.

How to use it in your React component:

import React from 'react'
import { useRectRef } from '@rs1/react-hooks'

export default props => {
    const [rect, sectionRef] = useRectRef()

    return (
        <div
            ref={sectionRef}
        >{`I'm ${rect.width}px x ${rect.height}px, try resizing the window!`}</div>
    )
}

useRefEffect

Useful in combination with useDynamicRef to react to a DOM node change.

How to use it in your React component:

import React, { useReducer } from 'react'
import { useRefEffect, useDynamicRef } from '@rs1/react-hooks'

export default props => {
    const [header, toggle] = useReducer(s => !s, true)
    const [section, sectionRef] = useDynamicRef(null, false)

    useRefEffect(
        section,
        () => {
            console.log(`The section tag is now ${section?.tagName}`)
        },
        []
    )

    return (
        <div>
            <button onClick={toggle}>Toggle</button>
            <hr />
            {header ? (
                <header
                    ref={sectionRef}
                    style={{
                        width: '100%',
                        minHeight: '200px',
                        background: 'red',
                    }}
                />
            ) : (
                <footer
                    ref={sectionRef}
                    style={{
                        width: '100%',
                        minHeight: '200px',
                        background: 'blue',
                    }}
                />
            )}
        </div>
    )
}

useSliderRef

Useful to transform any DOM node in a horizontal slider, handling touch and drag events.

How to use it in your React component:

import React from 'react'
import { useSliderRef } from '@rs1/react-hooks'

export default props => {
    const [value, sliderRef] = useSliderRef(
        /* Initial ref value: */ null,
        /* Initial slider value: */ 0,
        /* Slider direction: */ 'LTR'
    )
    /*
     * The slider direction should be one of:
     * 'LTR', 'RTL', 'TTB' or 'BTT'.
     * 'LTR': horizontal - left to right (default)
     * 'RTL': horizontal - right to left
     * 'TTB': vertical - top to bottom
     * 'BTT': vertical - bottom to top
     */

    return (
        <div>
            <p>
                {`Touch me or drag me to change my value! Actually I'm ${Math.round(
                    value * 100
                )}%`}
            </p>
            <div
                ref={sliderRef}
                style={{
                    width: '100%',
                    height: '10px',
                    background: 'red',
                    cursor: 'pointer',
                }}
            />
            <div
                style={{
                    width: `${Math.round(value * 100)}%`,
                    height: '10px',
                    background: 'blue',
                    marginTop: '-10px',
                    pointerEvents: 'none',
                }}
            />
        </div>
    )
}