// Use this
onClick={deferCall(fn, ⭐, 🔥, 🚀)}
// Instead of this
onClick={() => fn(😭, 😢,😞)}
deferCall
Documentation
The deferCall
utility is a higher-order function that defers the execution of a given function until the returned function is called. It supports synchronous and asynchronous functions.
Installation
yarn add just-defer-call
# or
npm i just-defer-call
Usage
Signature
export const deferCall =
<T, Args extends unknown[]>(
fn: (...args: Args) => T | Promise<T>,
...args: Args
): (() => T | Promise<T>) =>
(): T | Promise<T> => {
return fn(...args);
};
Parameters
fn
: The function to be deferred. It can be synchronous or asynchronous.args
: The arguments to pass tofn
when the deferred function is executed.
Returns
A new function that, when called, invokes the original function fn
with the provided arguments and returns the result of original fn
.
Examples
Vanilla Example 1 - Simple deferred call
import { deferCall } from "just-defer-call";
function greet(name) {
console.log(`Hello, ${name}!`);
}
const deferredGreet = deferCall(greet, 'Alice');
// Function execution is deferred
deferredGreet(); // Logs: "Hello, Alice!"
Vanilla Example 2 - pass as many arguments as you want
import { deferCall } from "just-defer-call";
function max(...args) {
console.log(`Max is: ${Math.max.apply(null, args)}!`);
}
const deferredMax = deferCall(max, 1, 2, 5, 4, 3);
// Function execution is deferred
deferredMax(); // Logs: "Max is: 5!"
Vanilla Example 3 - onClick handler
import { deferCall } from "just-defer-call";
function onClickHandler(buttonIndex) {
alert(`Pressed button index: ${buttonIndex}`);
}
document.querySelectorAll("button").forEach((element, index) => {
element.addEventListener('click', deferCall(onClickHandler, index));
});
TypeScript Example
import { deferCall } from "just-defer-call";
function add(a: number, b: number): number {
return a + b;
}
const deferredAdd = deferCall(add, 5, 10);
// Function execution is deferred
console.log(deferredAdd()); // Logs: 15
async function fetchData(url: string): Promise<string> {
const response = await fetch(url);
return response.text();
}
const deferredFetch = deferCall(fetchData, 'https://api.example.com');
// Deferred execution of async function
deferredFetch().then(data => console.log(data));
React Example
import React, { useState } from 'react';
import { deferCall } from "just-defer-call";
function App() {
const [count, setCount] = useState(0);
const increment = (value: number) => {
setCount((prev) => prev + value);
};
return (
<div>
<p>Count: {count}</p>
<button onClick={deferCall(increment, 1)}>Increment</button>
</div>
);
}
export default App;
Notes
- If
fn
is asynchronous, the returned function also returns a promise. Make sure to handle it appropriately. - Useful for event handling, lazy evaluations, or deferring expensive computations.
Testing
Jest Test Case
function sum(a: number, b: number): number {
return a + b;
}
async function stupidFunction(value: number): Promise<number> {
void 1;
return value;
}
describe("deferCall", () => {
test("deferCall should defer a synchronous function", () => {
const mockFn = jest.fn(sum);
const deferred = deferCall(mockFn, 2, 3);
expect(mockFn).not.toHaveBeenCalled();
deferred();
expect(mockFn).toHaveBeenCalledWith(2, 3);
});
test("deferCall should defer an asynchronous function", async () => {
const mockFn = jest.fn(stupidFunction);
const deferred = deferCall(mockFn, 42);
expect(mockFn).not.toHaveBeenCalled();
await deferred();
expect(mockFn).toHaveBeenCalledWith(42);
});
test("use deferCall with real function", async () => {
const value1 = Math.random();
const value2 = Math.random();
const valuesSum = value1 + value2;
const deferred = deferCall(sum, value1, value2);
expect(deferred()).toEqual(valuesSum);
});
test("use deferCall with real async function", async () => {
const value = Math.random();
const deferred = deferCall(stupidFunction, value);
expect(await deferred()).toEqual(value);
});
});