Skip to main content
react-effect-and-handler-patterns React effect and event handler optimization — narrow deps, handler refs, useEffectEvent, global listener dedup, one-time init. Use when writing or fixing useEffect, event handlers, or component initialization.
跳到安装 Skills Marketplace 发现并探索由社区构建的 Agent Skills
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/ihj04982/my-cursor-settings --skill react-effect-and-handler-patterns命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
下载 Zip 下载中... Use when the task asks for a visually strong landing page, website, app, prototype, demo, or game UI. This skill enforces restrained composition, image-led hierarchy, cohesive content structure, and tasteful motion while avoiding generic cards, weak branding, and UI clutter.
name react-effect-and-handler-patterns description React effect and event handler optimization — narrow deps, handler refs, useEffectEvent, global listener dedup, one-time init. Use when writing or fixing useEffect, event handlers, or component initialization.
React Effect & Handler Patterns
Patterns for efficient useEffect usage, stable event handler references, and proper initialization.
1. Narrow Effect Dependencies
Specify primitive dependencies instead of objects to minimize effect re-runs.
Bad (re-runs on any user field change):
useEffect (() => {
console .log (user.id );
}, [user]);
Good (re-runs only when id changes):
useEffect (() => {
console .log (user.id );
}, [user.id ]);
Derive booleans from continuous values:
useEffect (() => {
if (width < 768 ) enableMobileMode ();
}, [width]);
const isMobile = width < 768 ;
( {
(isMobile) ();
}, [isMobile]);
useEffect
() =>
if
enableMobileMode
2. Put Interaction Logic in Event Handlers If a side effect is triggered by a specific user action, run it in the handler — not as state + effect.
Bad (event modeled as state + effect):
const [submitted, setSubmitted] = useState (false );
const theme = useContext (ThemeContext );
useEffect (() => {
if (submitted) {
post ('/api/register' );
showToast ('Registered' , theme);
}
}, [submitted, theme]);
const theme = useContext (ThemeContext );
function handleSubmit ( ) {
post ('/api/register' );
showToast ('Registered' , theme);
}
3. Store Event Handlers in Refs Store callbacks in refs when used in effects that shouldn't re-subscribe on callback changes.
Bad (re-subscribes on every render):
function useWindowEvent (event : string , handler : (e) => void ) {
useEffect (() => {
window .addEventListener (event, handler);
return () => window .removeEventListener (event, handler);
}, [event, handler]);
}
Good (stable subscription):
function useWindowEvent (event : string , handler : (e) => void ) {
const handlerRef = useRef (handler);
useEffect (() => { handlerRef.current = handler; }, [handler]);
useEffect (() => {
const listener = (e ) => handlerRef.current (e);
window .addEventListener (event, listener);
return () => window .removeEventListener (event, listener);
}, [event]);
}
4. useEffectEvent for Stable Callback Refs useEffectEvent provides a cleaner API: a stable function that always calls the latest handler without being in effect deps.
Bad (effect re-runs on every callback change):
useEffect (() => {
const timeout = setTimeout (() => onSearch (query), 300 );
return () => clearTimeout (timeout);
}, [query, onSearch]);
import { useEffectEvent } from 'react' ;
const onSearchEvent = useEffectEvent (onSearch);
useEffect (() => {
const timeout = setTimeout (() => onSearchEvent (query), 300 );
return () => clearTimeout (timeout);
}, [query]);
5. Deduplicate Global Event Listeners Register global event listeners once and share across component instances.
Bad (N instances = N listeners):
function useKeyboardShortcut (key : string , callback : () => void ) {
useEffect (() => {
const handler = (e : KeyboardEvent ) => {
if (e.metaKey && e.key === key) callback ();
};
window .addEventListener ('keydown' , handler);
return () => window .removeEventListener ('keydown' , handler);
}, [key, callback]);
}
Good (N instances = 1 listener via module-level Map + useSWRSubscription):
const keyCallbacks = new Map <string , Set <() => void >>();
function useKeyboardShortcut (key : string , callback : () => void ) {
useEffect (() => {
if (!keyCallbacks.has (key)) keyCallbacks.set (key, new Set ());
keyCallbacks.get (key)!.add (callback);
return () => {
const set = keyCallbacks.get (key);
if (set) { set.delete (callback); if (set.size === 0 ) keyCallbacks.delete (key); }
};
}, [key, callback]);
useSWRSubscription ('global-keydown' , () => {
const handler = (e : KeyboardEvent ) => {
if (e.metaKey && keyCallbacks.has (e.key ))
keyCallbacks.get (e.key )!.forEach ((cb ) => cb ());
};
window .addEventListener ('keydown' , handler);
return () => window .removeEventListener ('keydown' , handler);
});
}
6. Initialize App Once, Not Per Mount App-wide init (analytics, auth check) must run once at module load, not inside useEffect([]) which re-runs on remount.
Bad (runs twice in dev, re-runs on remount):
function Comp ( ) {
useEffect (() => {
loadFromStorage ();
checkAuthToken ();
}, []);
}
let didInit = false ;
function Comp ( ) {
useEffect (() => {
if (didInit) return ;
didInit = true ;
loadFromStorage ();
checkAuthToken ();
}, []);
}
Checklist