
This tutorial demonstrates the ideal use case for the experimental useEffectEvent hook: creating stable, non-reactive callbacks that access fresh state or props without forcing your primary useEffect hook to re-run.
The Goal: Stable Auto-Saving
We want an editor component that automatically saves its content to an API every 5 seconds.
-
Requirement 1 (Stability): The 5-second timer/interval must be set up only once when the component mounts. It should not reset every time the user types.
-
Requirement 2 (Freshness): The save function must always read the latest text the user has typed (the latest
contentstate).
Step-by-Step Implementation
Step 0: Setup and API Mock
We start with the basic component structure and mock the API function that handles the saving logic.
import { useEffect, useState, useEffectEvent } from 'react';
// Mock API function for demonstration
const api = {
save: (data) => console.log('Saving latest content:', data.substring(0, 20) + '...')
};
function DocumentEditor() {
const [content, setContent] = useState('');
// ... (Steps 1 & 2 will go here)
return (
<div>
<h3>Document Editor (Saves every 5s)</h3>
<textarea
value={content}
onChange={(e) => setContent(e.target.value)}
rows="10"
cols="50"
placeholder="Start typing..."
/>
</div>
);
}
Step 1: Define the Effect Event (The Non-Reactive Action)
The core saving logic is defined here. By wrapping it in useEffectEvent, we ensure that the function doSave always has a stable identity across re-renders, yet the code inside (which uses content) always sees the latest state.
// 1. Define the Effect Event (The Stable Function)
const doSave = useEffectEvent(() => {
// ✅ This function ALWAYS sees the LATEST 'content' state.
if (content.trim() !== '') {
api.save(content);
}
});
-
Benefit: This function can be safely called from inside
useEffectwithout being listed as a dependency.
Step 2: Define the Effect (The Reactive Setup)
Now we set up the interval using a standard useEffect. Since the function we call (doSave) is stable, the dependency array remains empty, fulfilling our stability requirement.
// 2. Define the Effect (The Setup Logic)
useEffect(() => {
console.log('--- Setting up auto-save interval ---');
// The interval calls the stable 'doSave' event
const intervalId = setInterval(() => {
doSave(); // Calls the stable event which reads the fresh 'content'
}, 5000);
// Cleanup: Clears the interval when the component unmounts or effect re-runs
return () => {
console.log('--- Clearing auto-save interval ---');
clearInterval(intervalId);
};
}, []); // ✅ Dependency Array: Empty means it only runs ONCE on mount.
-
Result: The
useEffectsets the timer once. Typing into the textarea updatescontent, but it does not trigger theuseEffectto re-run and reset the timer.
Every 5 seconds, the stable doSave function runs and accesses the current, fresh content.
Summary of the Complete Component:
import { useEffect, useState, useEffectEvent } from 'react';
const api = {
save: (data) => console.log('Saving latest content:', data.substring(0, 20) + '...')
};
function DocumentEditor() {
const [content, setContent] = useState('');
// 1. STABLE ACTION: This reads fresh state (content) but has a fixed identity.
const doSave = useEffectEvent(() => {
if (content.trim() !== '') {
api.save(content);
}
});
// 2. STABLE SETUP: This sets up the non-restarting timer.
useEffect(() => {
console.log('--- Setting up auto-save interval ---');
const intervalId = setInterval(() => {
doSave(); // Calls the stable event
}, 5000);
return () => {
console.log('--- Clearing auto-save interval ---');
clearInterval(intervalId);
};
}, []); // Only runs once.
return (
<div>
<h3>Document Editor (Saves every 5s)</h3>
<textarea
value={content}
onChange={(e) => setContent(e.target.value)}
rows="10"
cols="50"
placeholder="Start typing..."
/>
</div>
);
}
Have any questions about the code above?
Leave them in the comments section below!
Useful links below:
Let me & my team build you a money making website/blog for your business https://bit.ly/tnrwebsite_service
Get Bluehost hosting for as little as $1.99/month (save 75%)…https://bit.ly/3C1fZd2
Best email marketing automation solution on the market! http://www.aweber.com/?373860
Build high converting sales funnels with a few simple clicks of your mouse! https://bit.ly/484YV29
Join my Patreon for one-on-one coaching and help with your coding…https://www.patreon.com/c/TyronneRatcliff
Buy me a coffee https://buymeacoffee.com/tyronneratcliff



