react-timing-hooks
Advanced tools
Comparing version 3.1.5 to 3.2.0
@@ -62,20 +62,59 @@ import { useRef, useCallback, useEffect, useState } from 'react'; | ||
const useInterval = (callback, delay) => { | ||
const [isPaused, setIsPaused] = useState(false); | ||
const [isStopped, setIsStopped] = useState(false); | ||
const intervalActive = useRef(true); | ||
const intervalCallback = useRef(callback); | ||
const intervalId = useRef(null); | ||
const pause = useCallback(() => { | ||
intervalActive.current = false; | ||
setIsPaused(true); // notify interval owner | ||
}, [setIsPaused]); | ||
const resume = useCallback(() => { | ||
intervalActive.current = true; | ||
setIsPaused(false); // notify interval owner | ||
}, [setIsPaused]); | ||
const stop = useCallback(() => { | ||
setIsStopped(true); | ||
}, [setIsStopped]); | ||
const start = useCallback(() => { | ||
intervalActive.current = true; | ||
setIsPaused(false); | ||
setIsStopped(false); | ||
}, [setIsStopped]); | ||
useEffect(() => { | ||
intervalCallback.current = callback; | ||
}, [callback]); | ||
const onIntervalStep = useCallback(() => { | ||
if (intervalActive.current === true) { | ||
intervalCallback.current(); | ||
} | ||
}, [intervalCallback, intervalActive]); | ||
useEffect(() => { | ||
if (delay !== null) { | ||
const id = setInterval(() => intervalCallback.current(), delay); | ||
return () => clearInterval(id); | ||
if (delay !== null && !isStopped) { | ||
intervalId.current = setInterval(onIntervalStep, delay); | ||
return () => clearInterval(intervalId.current); | ||
} | ||
}, [delay]); | ||
}, [delay, isStopped]); | ||
return { | ||
isPaused, | ||
isStopped, | ||
pause, | ||
resume, | ||
stop, | ||
start, | ||
}; | ||
}; | ||
const useTimer = (start = 0) => { | ||
const useCounter = (settings) => { | ||
const { start = 0, interval = 1000, stepSize = 1 } = settings; | ||
const [val, setVal] = useState(start); | ||
useInterval(() => setVal(val + 1), 1000); | ||
return val; | ||
const intervalControls = useInterval(() => setVal(val + stepSize), interval); | ||
return [val, intervalControls]; | ||
}; | ||
const useTimer = (start = 0) => { | ||
const [value] = useCounter({ start, interval: 1000, stepSize: 1 }); | ||
return value; | ||
}; | ||
const useIdleCallbackEffect = (effect, deps) => { | ||
@@ -82,0 +121,0 @@ if (!window.requestIdleCallback) { |
@@ -66,20 +66,59 @@ 'use strict'; | ||
const useInterval = (callback, delay) => { | ||
const [isPaused, setIsPaused] = react.useState(false); | ||
const [isStopped, setIsStopped] = react.useState(false); | ||
const intervalActive = react.useRef(true); | ||
const intervalCallback = react.useRef(callback); | ||
const intervalId = react.useRef(null); | ||
const pause = react.useCallback(() => { | ||
intervalActive.current = false; | ||
setIsPaused(true); // notify interval owner | ||
}, [setIsPaused]); | ||
const resume = react.useCallback(() => { | ||
intervalActive.current = true; | ||
setIsPaused(false); // notify interval owner | ||
}, [setIsPaused]); | ||
const stop = react.useCallback(() => { | ||
setIsStopped(true); | ||
}, [setIsStopped]); | ||
const start = react.useCallback(() => { | ||
intervalActive.current = true; | ||
setIsPaused(false); | ||
setIsStopped(false); | ||
}, [setIsStopped]); | ||
react.useEffect(() => { | ||
intervalCallback.current = callback; | ||
}, [callback]); | ||
const onIntervalStep = react.useCallback(() => { | ||
if (intervalActive.current === true) { | ||
intervalCallback.current(); | ||
} | ||
}, [intervalCallback, intervalActive]); | ||
react.useEffect(() => { | ||
if (delay !== null) { | ||
const id = setInterval(() => intervalCallback.current(), delay); | ||
return () => clearInterval(id); | ||
if (delay !== null && !isStopped) { | ||
intervalId.current = setInterval(onIntervalStep, delay); | ||
return () => clearInterval(intervalId.current); | ||
} | ||
}, [delay]); | ||
}, [delay, isStopped]); | ||
return { | ||
isPaused, | ||
isStopped, | ||
pause, | ||
resume, | ||
stop, | ||
start, | ||
}; | ||
}; | ||
const useTimer = (start = 0) => { | ||
const useCounter = (settings) => { | ||
const { start = 0, interval = 1000, stepSize = 1 } = settings; | ||
const [val, setVal] = react.useState(start); | ||
useInterval(() => setVal(val + 1), 1000); | ||
return val; | ||
const intervalControls = useInterval(() => setVal(val + stepSize), interval); | ||
return [val, intervalControls]; | ||
}; | ||
const useTimer = (start = 0) => { | ||
const [value] = useCounter({ start, interval: 1000, stepSize: 1 }); | ||
return value; | ||
}; | ||
const useIdleCallbackEffect = (effect, deps) => { | ||
@@ -86,0 +125,0 @@ if (!window.requestIdleCallback) { |
@@ -0,1 +1,9 @@ | ||
export type IntervalControls = { | ||
isPaused: boolean; | ||
isStopped: boolean; | ||
pause: () => void; | ||
resume: () => void; | ||
stop: () => void; | ||
start: () => void; | ||
}; | ||
/** | ||
@@ -8,3 +16,3 @@ * This hook was inspired by Dan Abramov's blogpost: | ||
*/ | ||
declare const useInterval: <T extends (...args: never[]) => unknown>(callback: T, delay: number | null) => void; | ||
declare const useInterval: <T extends (...args: never[]) => unknown>(callback: T, delay: number | null) => IntervalControls; | ||
export default useInterval; |
{ | ||
"name": "react-timing-hooks", | ||
"version": "3.1.5", | ||
"version": "3.2.0", | ||
"description": "React hooks for setTimeout, setInterval, requestAnimationFrame, requestIdleCallback", | ||
@@ -49,2 +49,3 @@ "main": "dist/index.js", | ||
"@shopify/jest-dom-mocks": "^4.0.0", | ||
"@testing-library/jest-dom": "^5.16.5", | ||
"@testing-library/react": "^13.4.0", | ||
@@ -51,0 +52,0 @@ "@testing-library/user-event": "^14.3.0", |
@@ -19,11 +19,9 @@ <img alt="logo" src="https://github.com/EricLambrecht/react-timing-hooks/raw/master/logo.png" width="680" /> | ||
* `requestIdleCallback()` | ||
* **Versatile API**, often including "callback" _and_ "effect" versions | ||
* Additional **utility hooks** like `useTimer`, `useAnimationFrameLoop` or `useClock` | ||
* Ability to **pause and resume intervals** | ||
* Additional **utility hooks** for timers, countdowns, display of time, or rendering (e.g. `useAnimationFrameLoop`) | ||
* A **versatile API**: customizable settings, different versions of the same hook (e.g. "useEffect" and "useCallback" versions). | ||
* Quality of Life: **Automatic clean-ups** of pending timers, intervals etc. (e.g. if your component un-mounts before a timer triggers), callbacks are **automatically memoized** | ||
* Full **Typescript** support | ||
* **[Lightweight](https://bundlephobia.com/result?p=react-timing-hooks)** (less than 1KB minzipped, no transitive dependencies!) | ||
* **Tree-shakable** — You only bundle what you use! | ||
* ... and it **saves a lot of code** | ||
* **Automatic clean-ups** of pending timers, intervals etc. (e.g. if your component un-mounts before a timer triggers) | ||
* callbacks are automatically **memoized** | ||
* All hooks are already **tested** | ||
@@ -48,3 +46,3 @@ | ||
#### `useTimeout()`: Delay a button click action | ||
#### Debouncing a button click with `useTimeout()` | ||
```jsx harmony | ||
@@ -54,3 +52,3 @@ import { useState } from 'react' | ||
const TimeoutRenderer = () => { | ||
const HelloWorld = () => { | ||
const [output, setOutput] = useState(null) | ||
@@ -66,14 +64,12 @@ const onButtonClick = useTimeout(() => setOutput('Hello World'), 1000) | ||
#### `useTimeout()`: Delay a button click action | ||
#### A resumable interval with `useInterval()` | ||
```jsx harmony | ||
import { useState } from 'react' | ||
import { useTimeout } from 'react-timing-hooks' | ||
import { useInterval } from 'react-timing-hooks' | ||
const TimeoutRenderer = () => { | ||
const [output, setOutput] = useState(null) | ||
const onButtonClick = useTimeout(() => setOutput('Hello World'), 1000) | ||
const StatusLogger = () => { | ||
const { isPaused, pause, resume } = useInterval(() => console.log('status update'), 1000) | ||
return <div> | ||
<button onClick={onButtonClick}>Start timeout!</button> | ||
<p>{output}</p> | ||
<button onClick={isPaused ? resume : pause}>Toggle Status Update</button> | ||
</div> | ||
@@ -83,3 +79,3 @@ } | ||
#### `useTimer`: Display how long the user has been browsing | ||
#### Display how long the user has been browsing using `useTimer()` | ||
```jsx harmony | ||
@@ -95,3 +91,3 @@ import { useState } from 'react' | ||
#### `useClock`: Display the current time, in real-time | ||
#### Display the current time with `useClock()` | ||
```jsx harmony | ||
@@ -109,3 +105,3 @@ import { useState } from 'react' | ||
#### `useAnimationFrameLoop`: Create an animation frame loop | ||
#### Create canvas renderer using the animation frame loop hook | ||
@@ -116,20 +112,27 @@ ```jsx harmony | ||
const AnimationFrameCounter = ({ depA, depB }) => { | ||
const [count, setCount] = useState(0) | ||
const Renderer = () => { | ||
const [stop, setStop] = useState(false) | ||
const delta = useRef(0) | ||
const canvasRef = useRef(null) | ||
const canvas = canvasRef.current | ||
const context = canvas.getContext('2d') | ||
const updateCanvas = (d) => { | ||
context.fillStyle = '#000000' | ||
context.fillRect(d, d, context.canvas.width, context.canvas.height) | ||
} | ||
useAnimationFrameLoop(() => { | ||
setCount(count + 1) | ||
delta.current += 1 | ||
updateCanvas(delta.current) | ||
}, stop) | ||
return ( | ||
<div> | ||
<p>{count}</p> | ||
<button onClick={() => setStop(!stop)}> | ||
Stop counting | ||
</button> | ||
</div> | ||
) | ||
return <> | ||
<canvas ref={canvasRef} {...props}/> | ||
<button onClick={() => setStop(!stop)}> | ||
Stop rendering | ||
</button> | ||
</> | ||
} | ||
``` | ||
``` | ||
@@ -136,0 +139,0 @@ ## Why does this exist? |
Sorry, the diff of this file is not supported yet
21
552
270
34692
26