
Research
Malicious npm Packages Impersonate Flashbots SDKs, Targeting Ethereum Wallet Credentials
Four npm packages disguised as cryptographic tools steal developer credentials and send them to attacker-controlled Telegram infrastructure.
@donisaac/react-hooks
Advanced tools
A toolkit of useful React hooks.
useFetch(url, fetchOptions)
useMemoCompare(curr, compare)
useDelayedCallback(cb, opts)
useInterval(callback, delay)
useMeasuredCallback(callback, deps, onMeasure?)
useMount(effect)
useDidMount()
useForceUpdate()
useLocalStorage(key, initialValue?)
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.
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.
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.
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.
TODO
useInterval(callback, delay)
Uses setInterval()
to repeatedly call callback
every delay
milliseconds.
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.
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()
.
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.
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.
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.
FAQs
A library of assorted React hooks
The npm package @donisaac/react-hooks receives a total of 0 weekly downloads. As such, @donisaac/react-hooks popularity was classified as not popular.
We found that @donisaac/react-hooks demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Four npm packages disguised as cryptographic tools steal developer credentials and send them to attacker-controlled Telegram infrastructure.
Security News
Ruby maintainers from Bundler and rbenv teams are building rv to bring Python uv's speed and unified tooling approach to Ruby development.
Security News
Following last week’s supply chain attack, Nx published findings on the GitHub Actions exploit and moved npm publishing to Trusted Publishers.