Collection of regularly used custom hooks as utils for React
- Collection of 22+ hooks as separate modules
- Standalone package with all the hooks at one place
- CommonJS, UMD and ESM Support
npm i -s @rooks/use-did-mount
npm i -s @rooks/use-interval
import useDidMount from "@rooks/use-did-mount";
npm i - s rooks
Import any hook from "rooks" and start using them!
import { useDidMount } from "rooks";
function App() {
useDidMount(() => {
alert("mounted");
});
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
</div>
);
}
npm install --save @rooks/use-boundingclientrect
import useBoundingclientrect from "@rooks/use-boundingclientrect"
function Demo() {
const myRef = useRef();
const getBoundingClientRect = useBoundingclientrect(myRef);
const [XOffset, setXOffset] = useState(0);
const [YOffset, setYOffset] = useState(300);
const displayString = JSON.stringify(getBoundingClientRect, null, 2);
return (
<>
<div
style={{
width: 300,
background: "lightblue",
padding: "10px",
position: "absolute",
left: XOffset,
top: YOffset
}}
ref={myRef}
>
<div
style={{
resize: "both",
overflow: "auto",
background: "white",
color: "blue",
maxWidth: "100%"
}}
>
<p>
Resize this div as you see fit. To demonstrate that it also updates
on child dom nodes resize
</p>
</div>
<h2>Bounds</h2>
<p>
<button onClick={() => setXOffset(XOffset - 5)}> Move Left </button>
<button onClick={() => setXOffset(XOffset + 5)}> Move Right </button>
</p>
<p>
<button onClick={() => setYOffset(YOffset - 5)}> Move Up </button>
<button onClick={() => setYOffset(YOffset + 5)}> Move Down </button>
</p>
</div>
<div style={{ height: 500 }}>
<pre>{displayString}</pre>
</div>
</>
);
}
render(<Demo/>)
Argument | Type | Description |
---|---|---|
ref | React ref | React ref whose boundingClientRect is to be found |
Return value | Type | Description | Default value |
---|---|---|---|
value | DOMRect | DOMRect Object containing x,y, width, height, left,right,top and bottom keys | null |
npm install --save @rooks/use-counter
import useCounter from "@rooks/use-counter"
function CounterComponent() {
const {
value,
increment,
decrement,
incrementBy,
decrementBy,
reset
} = useCounter(3);
function incrementBy5(){
incrementBy(5)
}
function decrementBy7(){
decrementBy(7)
}
return <>
Current value is {value}
<hr/>
<button onClick={increment}>increment</button>
<button onClick={decrement}>decrement</button>
<button onClick={incrementBy5} >incrementBy5</button>
<button onClick={decrementBy7} >decrementBy7</button>
<hr/>
<button onClick={reset}>reset</button>
</>;
}
render(<CounterComponent/>)
Argument | Type | Description |
---|---|---|
initialValue | number | Initial value of the counter |
Return value | Type | Description |
---|---|---|
counter | Object | Object containing {value,increment,decrement,incrementBy,decrementBy,reset} |
npm install --save @rooks/use-did-mount
import useDidMount from "@rooks/use-did-mount"
function Demo() {
useDidMount(function(){
console.log("mounted")
});
return null
}
render(<Demo/>)
Argument | Type | Description |
---|---|---|
callback | function | function to be called on mount |
npm install --save @rooks/use-did-update
import useDidUpdate from "@rooks/use-did-update";
function Demo() {
const [value, setValue] = useState(0);
const [hasUpdated, setHasUpdated] = useState(false);
useDidUpdate(() => {
console.log("Update");
setHasUpdated(true);
}, [value]);
return (
<>
<button onClick={() => setValue(value + 1)}>Value is {value}</button>
<p>Has updated - {hasUpdated.toString()}</p>
<p>Please check the console for logs.</p>
</>
);
}
render(<Demo />);
Eslint config for rooks
const eslintConfig = require('eslint-config');
// TODO: DEMONSTRATE API
npm install --save @rooks/use-input
import useInput from "@rooks/use-input"
Base
function Demo() {
const myInput = useInput("hello");
return (
<div>
<input {...myInput} />
<p>
Value is <b>{myInput.value}</b>
</p>
</div>
);
}
render(<Demo/>)
With optional validator
function Demo() {
const myInput = useInput("hello", {
validate: (newValue) => newValue.length < 15
});
return (
<div>
<p> Max length 15 </p>
<input {...myInput} />
<p>
Value is <b>{myInput.value}</b>
</p>
</div>
);
}
render(<Demo/>)
Argument | Type | Description | Default value |
---|---|---|---|
initialValue | string | Initial value of the string | "" |
opts | object | Options | {} |
Option key | Type | Description | Default value |
---|---|---|---|
validate | function | Validator function which receives changed valued before update as well as current value and should return true or false | undefined |
Return value | Type | Description |
---|---|---|
{value, onChange} | Object | Object containing {value : "String", onChange: "function that accepts an event and updates the value of the string"} |
npm install --save @rooks/use-interval
import useInterval from "@rooks/use-interval"
function reducer(state, action) {
switch (action.type) {
case "increment":
return { count: state.count + 1 };
default:
return state;
}
}
function Demo() {
const [value, dispatcher] = useReducer(reducer, { count: 0 });
function increment() {
dispatcher({
type: "increment"
});
}
const { start, stop } = useInterval(() => {
increment();
}, 1000);
return (
<>
<p>value is {value.count}</p>
<button onClick={start}>Start</button>
<button onClick={stop}>Stop</button>
</>
);
}
render(<Demo/>)
Argument | Type | Description | Default value |
---|---|---|---|
callback | function | Function be invoked after each interval duration | undefined |
intervalDuration | number | Duration in milliseconds after which callback is invoked | undefined |
startImmediate | boolean | Should the timer start immediately or no | false |
Returned object attributes | Type | Description |
---|---|---|
start | function | Start the interval |
stop | function | Stop the interval |
intervalId | intervalId | IntervalId of the interval |
npm install --save @rooks/use-key
import useKey from "@rooks/use-key";
function Demo() {
const inputRef = useRef();
function windowEnter(e) {
console.log("[Demo 1] Enter key was pressed on window");
}
function vowelsEntered(e) {
console.log("[Demo 1] You typed a vowel");
}
function capitalVowelsEntered(e) {
console.log("[Demo 1] You typed a capital vowel");
}
// window is the target
useKey(["Enter"], windowEnter);
useKey(["a", "e", "i", "o", "u"], vowelsEntered, {
target: inputRef
});
useKey(["A", "E", "I", "O", "U"], capitalVowelsEntered, {
target: inputRef
});
return (
<>
<p>Press enter anywhere to trigger a console.log statement</p>
<p>Press a,e,i,o,u in the input to trigger a console.log statement</p>
<p>Press A,E,I,O,U in the input to trigger a different log statement</p>
<input ref={inputRef} />
</>
);
}
render(<Demo />);
function Demo() {
const inputRef = useRef();
function onKeyInteraction(e) {
console.log("[Demo 2]Enter key", e.type);
}
useKey(["Enter"], onKeyInteraction, {
target: inputRef,
eventTypes: ["keypress", "keydown", "keyup"]
});
return (
<>
<p>Try "Enter" Keypress keydown and keyup </p>
<p>
It will log 3 events on this input. Since you can listen to multiple
types of events on a keyboard key.
</p>
<input ref={inputRef} />
</>
);
}
render(<Demo />);
function Demo() {
const inputRef = useRef();
const [shouldListen, setShouldListen] = useState(false);
function toggleShouldListen() {
setShouldListen(!shouldListen);
}
function onKeyInteraction(e) {
console.log("[Demo 3] Enter key", e.type);
}
useKey(["Enter"], onKeyInteraction, {
target: inputRef,
eventTypes: ["keypress", "keydown", "keyup"],
when: shouldListen
});
return (
<>
<p>
Enter key events will only be logged when the listening state is true.
Click on the button to toggle between listening and not listening
states.{" "}
</p>
<p>
Handy for adding and removing event handlers only when certain
conditions are met.
</p>
<input ref={inputRef} />
<br />
<button onClick={toggleShouldListen}>
<b>{shouldListen ? "Listening" : "Not listening"}</b> - Toggle{" "}
</button>
</>
);
}
render(<Demo />);
Localstorage hook for React. Syncs with localstorage values across components and browser windows automatically.
Sets and retrieves a key from localStorage and subscribes to it for updates across windows.
npm install --save @rooks/use-localstorage
import useLocalstorage from "@rooks/use-localstorage";
function Demo() {
const { value, set, remove } = useLocalstorage("my-value", 0);
return (
<p>
Value is {value}{" "}
<button onClick={() => set(value !== null ? parseFloat(value) + 1 : 0)}>
Increment
</button>
<button onClick={remove}>Remove </button>
</p>
);
}
render(<Demo />);
npm install --save @rooks/use-mouse
import useMouse from "@rooks/use-mouse"
function Demo() {
const { x, y } = useMouse();
return (
<>
<p> Move mouse here to see changes to position </p>
<p>X position is {x || "null"}</p>
<p>X position is {y || "null"}</p>
</>
);
}
render(<Demo/>)
Returned object attributes | Type | Description |
---|---|---|
x | int | X position of mouse |
y | int | Y position of mouse |
npm install --save @rooks/use-mutation-observer
import useMutationObserver from "@rooks/use-mutation-observer"
function Demo() {
const myRef = useRef();
const [mutationCount, setMutationCount] = useState(0);
const incrementMutationCount = () => {
return setMutationCount(mutationCount + 1);
};
useMutationObserver(myRef, incrementMutationCount);
const [XOffset, setXOffset] = useState(0);
const [YOffset, setYOffset] = useState(300);
return (
<>
<div
style={{
width: 300,
background: "lightblue",
padding: "10px",
position: "absolute",
left: XOffset,
top: YOffset
}}
ref={myRef}
>
<div
style={{
resize: "both",
overflow: "auto",
background: "white",
color: "blue",
maxWidth: "100%"
}}
>
<p>
Resize this div as you see fit. To demonstrate that it also updates
on child dom nodes resize
</p>
</div>
<h2>Bounds</h2>
<p>
<button onClick={() => setXOffset(XOffset - 5)}> Move Left </button>
<button onClick={() => setXOffset(XOffset + 5)}> Move Right </button>
</p>
<p>
<button onClick={() => setYOffset(YOffset - 5)}> Move Up </button>
<button onClick={() => setYOffset(YOffset + 5)}> Move Down </button>
</p>
</div>
<div style={{ height: 500 }} onClick={incrementMutationCount}>
<pre>Mutation count {mutationCount}</pre>
</div>
</>
);
}
render(<Demo/>)
Argument | Type | Description | Default value |
---|---|---|---|
ref | React ref | Ref which should be observed for Mutations | undefined |
callback | function | Function which should be invoked on mutation. It is called with the mutationList and observer |
undefined |
config | object | Mutation Observer configuration | {attributes: true,,characterData: true,,subtree: true,,childList: true} |
npm install --save @rooks/use-navigator-language
import useNavigatorLanguage from "@rooks/use-navigator-language";
function Demo() {
const language = useNavigatorLanguage();
return <p>Language is {language}</p>;
}
render(<Demo />);
A language (String) is returned.
npm install --save @rooks/use-online
import useOnline from "@rooks/use-online";
function Demo() {
const isOnline = useOnline();
return <p>Online status - {isOnline.toString()}</p>;
}
render(<Demo />);
Offline status (boolean) is returned.
npm install --save @rooks/use-outside-click
import useOutsideClick from "@rooks/use-outside-click";
function Demo() {
const pRef = useRef();
function outsidePClick() {
alert("Clicked outside p");
}
useOutsideClick(pRef, outsidePClick);
return (
<div>
<p ref={pRef}>Click outside me</p>
</div>
);
}
render(<Demo />);
npm install --save @rooks/use-previous
import usePrevious from "@rooks/use-previous";
function Demo() {
const myInput = useInput("hello world");
const previousValue = usePrevious(myInput.value);
return (
<div>
<div>
<input {...myInput} />
</div>
<p>
Current value is <b>{myInput.value}</b>
</p>
<p>
Previous value was <b>{previousValue || "-"}</b>
</p>
</div>
);
}
render(<Demo />);
Argument | Type | Description |
---|---|---|
value | any | The variable whose previous value should be stored |
npm install --save @rooks/use-raf
import useRaf from "@rooks/use-raf";
let angle = 0;
function updateAngle() {
angle = (angle + 3) % 360;
return (angle * Math.PI) / 180;
}
function Demo() {
const { value: shouldRun, toggleValue: toggleShouldRun } = useToggle(true);
const myRef = useRef();
const canvasRef = useRef();
useRaf(() => {
if (canvasRef && canvasRef.current) {
const screenRatio = window.devicePixelRatio || 1;
let angle = updateAngle();
const canvas = canvasRef.current;
var ctx = canvas.getContext("2d");
ctx.save();
ctx.clearRect(0, 0, canvasRef.current.width, canvasRef.current.height);
ctx.scale(screenRatio, screenRatio);
ctx.fillStyle = "midnightblue";
ctx.globalAlpha = 1;
ctx.fillRect(0, 0, canvasRef.current.width, canvasRef.current.height);
ctx.fillStyle = "yellow";
ctx.lineWidth = 2;
ctx.translate(50, 50);
ctx.rotate(angle);
ctx.fillRect(-20, -20, 40, 40);
ctx.restore();
}
}, shouldRun);
return (
<>
<h2>
Request animation frame is now {shouldRun ? "" : "in"}active. Click to
toggle.
</h2>
<p>
<button onClick={toggleShouldRun}>Toggle Raf</button>{" "}
</p>
<canvas
ref={canvasRef}
style={{ height: `100px`, width: `100%`, background: "grey" }}
/>
</>
);
}
render(<Demo />);
Standalone build for all rooks. This package contains all the hooks built as part of the rooks project.
Note: If you only need a few hooks from the rooks package, it's prefereable to install individiual hooks from npm instead of the standalone rooks build. In other words, install @rooks/use-did-mount
instead of rooks
if you only need the use-did-mount
functionality.
npm i -s @rooks/use-did-mount
npm i -s @rooks/use-interval
import useDidMount from "@rooks/use-did-mount";
npm i - s rooks
Import any hook from "rooks" and start using them!
import { useDidMount } from "rooks";
function App() {
useDidMount(() => {
alert("mounted");
});
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
</div>
);
}
Feel free to join as contributors!
😄
npm install --save @rooks/use-select
import useSelect from "@rooks/use-select"
const list = [
{
heading: "Tab 1",
content: "Tab 1 Content"
},
{
heading: "Tab 2",
content: "Tab 2 Content"
}
];
function Demo() {
const { index, setIndex, item } = useSelect(list, 0);
return (
<div>
{list.map((listItem, listItemIndex) => (
<button
key={listItemIndex}
style={{
background: index === listItemIndex ? "dodgerblue" : "inherit"
}}
onClick={() => setIndex(listItemIndex)}
>
{listItem.heading}
</button>
))}
<p>{item.content}</p>
</div>
);
}
render(<Demo/>)
Argument | Type | Description | Default value |
---|---|---|---|
list | Array | List of items for which the selection is used | undefined |
initialIndex | number | Initially selected index | 0 |
Returned object attributes | Type | Description |
---|---|---|
index | int | Index of currently selected index |
item | any | Currently selected item |
setIndex | function | Update selected index |
setItem | function | Update selected item |
npm install --save @rooks/use-sessionstorage
import useSessionstorage from "@rooks/use-sessionstorage";
function Demo() {
const { value, set, remove } = useSessionstorage("my-value", 0);
return (
<p>
Value is {value}{" "}
<button onClick={() => set(value !== null ? parseFloat(value) + 1 : 0)}>
Increment
</button>
<button onClick={remove}>Remove </button>
</p>
);
}
render(<Demo />);
npm install --save @rooks/use-time-ago
import useTimeAgo from "@rooks/use-time-ago"
function Demo() {
const [date, setDate] = useState(new Date());
const timeAgo = useTimeAgo(date.getTime() - 1000 * 12, {
locale: "zh_CN"
});
const timeAgo2 = useTimeAgo(date.getTime() - 1000 * 12);
return (
<>
<p>{timeAgo}</p>
<p>{timeAgo2}</p>
</>
);
}
render(<Demo/>)
Argument | Type | Description | Default value |
---|---|---|---|
input | Date | Timestamp | etc |
options | Object | Options object | { intervalMs:0 } |
Options | Type | Description | Default value |
---|---|---|---|
intervalMs | milliseconds | Duration after which time-ago has to be calculated | 1000 |
locale | String | Locale in which value is expected | undefined |
relativeDate | Date | Relative date object with respect to which time-ago is to be calcuated | Current Time |
Timeago string is returned.
npm install --save @rooks/use-timeout
import useTimeout from "@rooks/use-timeout"
function TimeoutComponent() {
function doAlert() {
window.alert("timeout expired!");
}
const { start, clear } = useTimeout(doAlert, 2000);
return (
<>
<button onClick={start}> Start timeout </button>
<button onClick={clear}> Clear timeout </button>
</>
);
}
render(<TimeoutComponent/>)
Arguments | Type | Description | Default value |
---|---|---|---|
callback | function | Function to be executed in timeout | undefind |
delay | Number | Number in milliseconds after which callback is to be run | 0 |
Returned object attributes | Type | Description |
---|---|---|
clear | function | Clear the timeout |
start | function | Start the timeout |
npm install --save @rooks/use-toggle
import useToggle from "@rooks/use-toggle"
const customToggleFunction = v => (v === "start" ? "stop" : "start");
function Demo() {
const [value1, toggleValue1] = useToggle();
const [value2, toggleValue2] = useToggle(true);
const [value3, toggleValue3] = useToggle(
"start",
customToggleFunction
);
return (
<>
<section>
<h3>Base</h3>
<button onClick={toggleValue1}>{value1.toString()}</button>
<hr />
</section>
<section>
<h3>Initial true</h3>
<button onClick={toggleValue2}>{value2.toString()}</button>
<hr />
</section>
<section>
<h3>Custom values</h3>
<button onClick={toggleValue3}>{value3}</button>
</section>
</>
);
}
render(<Demo/>)
Arguments | Type | Description | Default value |
---|---|---|---|
initialValue | boolean | Initial value of the state | false |
toggleFunction | function | Function which determines how to toggle a value | v => !v |
Returned Array items | Type | Description |
---|---|---|
value | Any | Current value |
toggleValue | function | Toggle function which changes the value to the other value in the list of 2 acceptable values. (Mostly true or false) |
npm install --save @rooks/use-visibility-sensor
import useVisibilitySensor from "@rooks/use-visibility-sensor"
function Demo() {
const rootNode = useRef(null);
const { isVisible, visibilityRect } = useVisibilitySensor(rootNode, {
intervalCheck: false,
scrollCheck: true,
resizeCheck: true
});
return (
<div ref={rootNode}>
<p>
{isVisible ? "Visible" : isVisible === null ? "Null" : "Not Visible"}
</p>
</div>
);
}
render(<Demo/>)
It checks whether an element has scrolled into view or not. A lot of the logic is taken from react-visibility-sensor and is rewritten for the hooks proposal.
Note: This is using the new React Hooks API Proposal which is subject to change until React 16.7 final.
You'll need to install
react
,react-dom
, etc at^16.7.0-alpha.0
Returned object attributes | Type | Description |
---|---|---|
isVisible | Boolean | Is Ref visible or not |
visibilityRect | Object | VisibilityRectangle containing coordinates of the container |
The first argument of the useVisibilitySensor
hook is a ref, the second argument is an options object. The available options are as follow:
intervalCheck: false
- Accepts int | bool
, if an int
is supplied, that will be the interval in ms
and it keeps checking for visibility
partialVisibility: false
- Accepts bool | string
: Tells the hook if partial visibility should be considered as visibility or not. Accepts false
and directions top
, bottom
, left
and right
containment: null
- A DOMNode
element which defaults to window
. The element relative to which visibility is checked against
scrollCheck: true
- A bool
to determine whether to check for scroll behavior or not
scrollDebounce: 250
- The debounce ms for scroll
scrollThrottle: -1
- The throttle ms for scroll. If throttle > -1, debounce is ignored.
resizeCheck: false
- A bool
to determine whether to check for resize behavior or not
resizeDebounce: 250
- The debounce ms for resize
resizeThrottle: -1
- The throttle ms for resize. If throttle > -1, debounce is ignored.
shouldCheckOnMount: true
- A bool
which determines whether an initial check on first render should happen or not.
minTopValue: 0
- An int
top value to determine what amount of top visibility should be considered for visibility
- Init
- Scroll and Resize support
- Debounce and throttling
- Option to opt-out of initial check on mount
- Documentation of all options
- Tests _ WIP _
- More examples _ WIP _
npm install --save @rooks/use-will-unmount
import useWillUnmount from "@rooks/use-will-unmount"
function Message(){
useWillUnmount(function () {
alert("unmounted")
})
return <p> Message </p>
}
function Demo() {
const [
value,
changeValue
] = useState(true);
function toggleValue(){
changeValue(!value)
}
return <>
<p><button onClick={toggleValue}>Toggle show </button></p>
{value && <Message/>}
</>;
}
render(<Demo/>)
Arguments | Type | Description | Default value |
---|---|---|---|
callback | function | Callback function which needs to run on unmount | undefined |
npm install --save @rooks/use-window-size
import useWindowSize from "@rooks/use-window-size"
function WindowComponent() {
const { innerWidth, innerHeight, outerHeight, outerWidth } = useWindowSize();
return (
<div>
<p>
<span>innerHeight - </span>
<span>{innerHeight}</span>
</p>
<p>
<span>innerWidth - </span>
<span>{innerWidth}</span>
</p>
<p>
<span>outerHeight - </span>
<span>{outerHeight}</span>
</p>
<p>
<span>outerWidth - </span>
<span>{outerWidth}</span>
</p>
</div>
);
}
render(<WindowComponent/>)
Returned object attributes | Type | Description |
---|---|---|
width | int | inner width of window |
height | int | inner height of window |
outerWidth | int | outer height of window |
outerHeight | int | outer width of window |
npm install --save @rooks/use-worker
import useWorker from "@rooks/use-worker"
function Demo() {
const [value, setValue] = useState(0);
const [error, setError] = useState(null);
const worker = useWorker("/worker.js", {
onMessage: e => {
console.log("message received from worker");
console.log(e.data);
setValue(e.data);
},
onMessageError: e => {
console.log(e);
}
});
return value;
}
const rootElement = document.getElementById("root");
ReactDOM.render(<Demo />, rootElement);
Arguments | Type | Description | Default value |
---|---|---|---|
scriptPath | string | Path to the script file that a new Worker is to be created with | undefined |
options | Object | Options object within which onMessage and onMessageError options can be passed to communicate with the worker |
{onMessage: () => {},,onMessageError: () => {}} |
The worker instance is returned.