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

@donisaac/react-hooks

v1.2.1

Published

A library of assorted React hooks

Downloads

6

Readme

React Hooks

Gated
CI Maintainability Test Coverage

A toolkit of useful React hooks.

Hooks

useFetch(url, fetchOptions)

Makes a fetch request to a URL, returning information about the state of the request.

The state object contains three properties: status, data, and error. By checking status you can create lazy-loaded components.

  • While the request is in progress, status will be pending and both data and error will be null.

  • When the request completes, status will be success. The response payload will be parsed and stored in data.

  • If the request fails, status is set to error and the thrown error is stored in the error property.

Examples

This example creates a TODO list from data retrieved from your API. JSON data in the response body will be parsed into an object.

import { FC, useEffect } from 'react'
import useFetch, { RequestState, RequestStatus } from '@donisaac/react-hooks'

type TodoTasks = string[]

// Create a list of todo items from data fetched from your backend
const TodoList: FC = () => {
    const { status, data, error } = useFetch<TodoTasks>('https://my-api.com/todo')

    // Do _not_ use data in your dependency array! Instead, use status.
    useEffect(() => {
        console.log('Todo tasks: ', data)
    }, [status])

    if (status === 'pending') {
        // API request has not completed
        return <span>Getting tasks...</span>

    } else if (status === 'success') {
        // Successfully got data from API
        return (
            <ol id="tasks">
                {data.map(task => <li key={task}>{task}</li>)}
            </ol>
        )

    } else {
        // Fetch threw an error, usually because the API returned a 4xx or 5xx
        // response.
        return <span>Could not get task list: ${error.message}</span>
    }
}

This example gets a user's profile picture and displays it in an image. When response bodies contain images, they are parsed into Blobs.

import { FC, useState, useEffect } from 'react'
import useFetch, { RequestState, RequestStatus } from '@donisaac/react-hooks'

import defaultProfilePicture from './img/default-profile.png'

// Get the user's profile picture from your backend and display it as an image
const ProfilePicture: FC<{ username: string }> = ({ username }) => {
    const {
        status,
        data: profilePic,
        error
    } = useFetch<Blob>(`https://my-api.com/user/${username}/profile`)

    const [imageSrc, setImageSrc] = useState(defaultProfilePicture)

    useEffect(() => {
        if (status === 'success') {
            const imageUrl: string = URL.createObjectURL(data)
            setImageSrc(imageUrl)

        } else if (status === 'error') {
            // Log any errors that occur so we know there's a problem.
            // Don't change the profile picture - we'll fall back to the default
            // one.
            console.error(error)
        }
    }, [status])

    return (
        <img
            className="profile-pic"
            src={defaultProfilePicture}
            alt={`${username}'s profile picture`}
        />
    )
}

useMemoCompare(curr, compare)

Similar to useMemo, this hook memoizes an object by comparing it with a comparison function. This hook doesn't aim to avoid expensive computation, rather it aims to provide a stable value for a deeply nested object so that it can be used within a dependency array.

Comparison functions take two parameters of the same type and return true if they are the same and false if they are not. If a comparison function is not provided, it defaults to a limited deep equality check.

This hook was inspired by useHook's [useMemoCompare][https://usehooks.com/useMemoCompare], but the implementation is different.

Example

import { FC, useEffect } from 'react'
import { useMemoCompare } from '@donisaac/react-hooks'
import { makeApiCall, ApiCallPayload, ApiResult, arePayloadsEqual } from './api'

export type ConnectedComponentProps = {
    // Some deeply nested object to send to your API
    payload: APICallPayload
}

export const ConnectedComponent: FC<ConnectedComponentProps> = props => {
    const [apiResult, setApiResult = useState<ApiResult>()

    // Comparison function has type <T>(a: T, b: T) => boolean
    const payload = useMemoCompare(props.payload, arePayloadsEqual)

    // because `payload` is memoized, it is safe to use in a dependency array.
    // This useEffect will only be re-executed when `payload` is structurally
    // different.
    useEffect(() => {
        makeApiCall(payload).then(res => setApiResult(res))
    }, [payload])

    return apiResult
        ? <div>Loading...</div>
        : <div>Got result from API: {JSON.stringify(apiResult)}</div>
}

useDelayedCallback(cb, opts)

Similar to useCallback(), this hook returns a memoized callback whose execution is deferred. This allows you to run expensive or lazy code you don't need done immediately, keeping the main thread unblocked.

Delay Strategies

For a high-level description of Node's event loop, checkout this docs page.

To tell the hook how to defer the function, provide it a strategy. By default, idle is used. The available strategies are:

  • idle: The function will execute when the main thread isn't busy. You can provide a timeout to force execution, idle or not, after a certain amount of time. Uses useIdleCallback.

  • animation: The function will execute before the next repaint. Uses requestAnimationFrame.

  • timeout: The function will execute after timeout milliseconds. Uses setTimeout.

  • resolve: The function will execute after all remaining currently scheduled events have run. More specifically, execution is pushed to the end of the microtask queue for the current event loop stage by using Promise.resolve().then(). See this PR for microtask queue details.

  • resolve: The function's execution is pushed to the end of the event queue. It will execute within the event loop cycle it was called in, but after all other currently pending events have been processed. uses

NOTE: Some strategies rely on nitty-gritty details of the event loop. Because of this, runtime behavior may differ depending on where you're running your application.For example, Node uses libuv while Chrome uses libevent. You may find this StackOverflow post helpful.

The returned callback accepts the same parameters as the original callback, except it returns a cancelable Promise. This Promise resolves with the original callback's return value, or rejects if it throws. You can use its cancel() method to prevent execution if it hasn't run yet or prevent chained promises from being called if it has. Additionally, pending callbacks will be canceled when the component unmounts, making it safe to run useState() setters within your callback.

Example

TODO


useInterval(callback, delay)

Uses setInterval() to repeatedly call callback every delay milliseconds.

Example

import { FC, useState, useEffect } from 'react'
import useInterval from '@donisaac/react-hooks'

export const Timer: FC = () => {
    const [date, setDate] = useState(new Date())

    // Update date every second
    useInterval(() => {
        setDate(new Date())
    }, 1000)

    return (
        <span>
            {date.toLocaleTimeString()}
        </span>
    )
}

useMeasuredCallback(callback, deps, onMeasure?)

Similar to useCallback(), this hook returns a memoized callback whose execution time is recorded. When the returned callback is called, the onMeasure callback is invoked with the amount of time it took to run. Execution times are also displayed in the DevTools Performance Timeline. This lets you monitor time-consuming operations.

Example

import { FC, useCallback, useEffect } from 'react'
import useMeasuredCallback from '@donisaac/react-hooks'
import intensiveTask from './intensiveTask'

export const MyComponent: FC<{ logger?: typeof Console }> = ({
    logger = console
}) => {
    const [result, setResult] = useState<ReturnType<typeof intensiveTask> | undefined>(undefined)

    const onMeasure = useCallback((measure: PerformanceMeasure) => {
        logger.log(`intensive task took ${measure.duration} ms to run`)
    })

    const measuredIntensiveTask = useMeasuredCallback(
        intensiveTask,
        [],
        onMeasure
    )

    // Only call the function when needed (it takes a while to run!)
    useEffect(() => {
        setResult(measuredIntensiveTask())
    }, [measuredIntensiveTask])

    return <div>{result}</div>
}

useMount(effect)

Similar to useEffect(), but effect is only called once after the component has completed its initial render and has been mounted to the DOM. You can think of this as the hook version of componentDidMount().

Example

import { FC } from 'react'
import useMount from '@donisaac/react-hooks'

export const MyComponent: FC = () => {
    useMount(() => {
        console.log('component has been mounted to the DOM')

        return () => console.log('component has been unmounted from the DOM')
    })

    return <div />
}

useDidMount()

Similar to useMount(), except it returns a boolean that is set to true when the component mounts instead of calling an effect function.

Example

import { FC } from 'react'
import useDidMount from '@donisaac/react-hooks'

export const MyComponent: FC = () => {
    const isMounted = useDidMount()
    return (
        <span>MyComponent {isMounted ? 'is' : 'is not'} mounted to the DOM</span>
    )
}

useForceUpdate()

Allows you to force a component to rerender whether or not React has detected a state change. The function returned by this hook will cause the rerender when called.

Example

import { FC, useRef, useEffect } from 'react
import { useForceUpdate } from '@donisaac/react-hooks'
// Displays an update counter and a button to force an update. This is 
// not exactly best practice, but it is a good example of how to use
// this hook.
const TestComponent: FC = () => {
    const forceUpdate = useForceUpdate()
    const updateCount = useRef(0)
    // Increment the update counter on each rerender
    useEffect(() => {
       updateCount.current++
    })
    return (
        <div>
            <span data-testid="update-count" id="update-count">
                {updateCount.current}
            </span>
            <button onClick={forceUpdate}>Force Update</button>
        </div>
    )
}

useLocalStorage(key, initialValue?)

Similar to useState, but values are persisted in the browser's local storage.

When state is updated using the returned setter, the new value is saved to local storage under the given key. Setting the value to undefined will remove it from local storage. Local storage updates are performed lazily to prevent blocking the main rendering thread.

If provided, the initialState parameter will be used as the state's starting value. Otherwise existing data persisted in local storage will be used. If neither are available, the state will be undefined.

@param key - The key to store values under in local storage. @param initialState - Either a value or a factory function to use as the state's initial value.