As React developers, we often face scenarios where multiple rapid state changes need to be synchronized with an API. Making an API call for every tiny change can be inefficient and taxing on both the client and server. This is where debouncing and clever state management come into play. In this article, we'll build a custom React hook that captures parallel API update calls by merging payloads and debouncing the API call.
Imagine an input field where users can adjust settings or preferences. Each keystroke or adjustment could trigger an API call to save the new state. If a user makes several changes in quick succession, this could lead to a flood of API requests:
Debouncing is a technique used to limit the rate at which a function can fire. Instead of calling the function immediately, you wait for a certain period of inactivity before executing it. If another call comes in before the delay is over, the timer resets.
In React, useRef is a hook that allows you to persist mutable values between renders without triggering a re-render. It's essentially a container that holds a mutable value.
Let's dive into the code and understand how it all comes together.
import { debounce } from "@mui/material"; import { useCallback, useEffect, useRef } from "react"; type DebouncedUpdateParams = { id: string; params: Record<string, any>; }; function useDebouncedUpdate( apiUpdate: (params: DebouncedUpdateParams) => void, delay: number = 300, ) { const accumulatedUpdates = useRef<DebouncedUpdateParams | null>(null); const processUpdates = useRef( debounce(() => { if (accumulatedUpdates.current) { apiUpdate(accumulatedUpdates.current); accumulatedUpdates.current = null; } }, delay), ).current; const handleUpdate = useCallback( (params: DebouncedUpdateParams) => { accumulatedUpdates.current = { id: params.id, params: { ...(accumulatedUpdates.current?.params || {}), ...params.params, }, }; processUpdates(); }, [processUpdates], ); useEffect(() => { return () => { processUpdates.clear(); }; }, [processUpdates]); return handleUpdate; } export default useDebouncedUpdate;
We initialize a useRef called accumulatedUpdates to store the combined parameters of all incoming updates.
const accumulatedUpdates = useRef
We create a debounced function processUpdates using the debounce utility from Material UI.
const processUpdates = useRef( debounce(() => { if (accumulatedUpdates.current) { apiUpdate(accumulatedUpdates.current); accumulatedUpdates.current = null; } }, delay), ).current;
The handleUpdate function is responsible for accumulating updates and triggering the debounced API call.
const handleUpdate = useCallback( (params: DebouncedUpdateParams) => { accumulatedUpdates.current = { id: params.id, params: { ...(accumulatedUpdates.current?.params || {}), ...params.params, }, }; processUpdates(); }, [processUpdates], );
We clear the debounced function when the component unmounts to prevent memory leaks.
useEffect(() => { return () => { processUpdates.clear(); }; }, [processUpdates]);
Here's how you might use this hook in a component:
import React from "react"; import useDebouncedUpdate from "./useDebouncedUpdate"; function SettingsComponent() { const debouncedUpdate = useDebouncedUpdate(updateSettingsApi, 500); const handleChange = (settingName, value) => { debouncedUpdate({ id: "user-settings", params: { [settingName]: value }, }); }; return ( <div> <input type="text" onChange={(e) => handleChange("username", e.target.value)} /> <input type="checkbox" onChange={(e) => handleChange("notifications", e.target.checked)} /> </div> ); } function updateSettingsApi({ id, params }) { // Make your API call here console.log("Updating settings:", params); }
By combining debouncing with state accumulation, we can create efficient and responsive applications. The useDebouncedUpdate hook ensures that rapid changes are batched together, reducing unnecessary API calls and improving performance.
Key Takeaways:
Feel free to integrate this hook into your projects and adjust it to suit your specific needs. Happy coding!
The above is the detailed content of How I Optimized API Calls by in My React App. For more information, please follow other related articles on the PHP Chinese website!