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; }; function useDebouncedUpdate( apiUpdate: (params: DebouncedUpdateParams) => void, delay: number = 300, ) { const accumulatedUpdates = useRef (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 (handleChange("username", e.target.value)} /> handleChange("notifications", e.target.checked)} />); } 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!
Disclaimer: All resources provided are partly from the Internet. If there is any infringement of your copyright or other rights and interests, please explain the detailed reasons and provide proof of copyright or rights and interests and then send it to the email: [email protected] We will handle it for you as soon as possible.
Copyright© 2022 湘ICP备2022001581号-3