You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Context技术疑问:是否应在自定义Context中存储事件发射器?

How to Handle Menu Closure Alongside User Deletion in React Context

Great question! Let's walk through your options here—you're dealing with a common cross-component communication scenario where multiple actions (like deleting a user) need to trigger side effects in unrelated components (like closing a right-click menu). Let's break down the best approaches, including whether an event emitter makes sense.

Option 1: Extend Your Existing Context (Simplest Approach)

Since you already have a UserModificationContext for handling edit/delete actions, the most straightforward fix is to expand it to include callbacks related to your menu state. This keeps all related actions grouped together and aligns with how you're already using Context.

First, update your Context type to include a menu-related callback:

type UserModifcationContextType = {
  onEdit?: (objectId: number) => void;
  onDelete?: (objectId: number) => void;
  onCloseMenu?: () => void; // Add this menu closure callback
};

Then, update your provider to accept and pass through this new callback:

export const UserModificationContextProvider: React.FC<UserModifcationContextType> = ({ 
  onDelete, 
  onEdit,
  onCloseMenu // Include the new prop
}) => {
  return (
    <UserModificationContext.Provider 
      value={{ onDelete, onEdit, onCloseMenu }} 
    /> 
  );
}

In your right-click menu's parent component, pass the menu-close function to the provider:

// In the component that manages the menu open/close state
const [isMenuOpen, setIsMenuOpen] = useState(false);

return (
  <UserModificationContextProvider
    onDelete={handleDeleteUser}
    onEdit={handleEditUser}
    onCloseMenu={() => setIsMenuOpen(false)} // Pass the close function here
  >
    {/* Your menu and nested components live here */}
  </UserModificationContextProvider>
);

Finally, when you trigger a delete from the menu (or anywhere else), call both onDelete and onCloseMenu:

// Inside your delete button/menu item component
const { onDelete, onCloseMenu } = useContext(UserModificationContext);

const handleDelete = (userId: number) => {
  onDelete?.(userId);
  onCloseMenu?.(); // Close the menu immediately after deletion
};

This approach is simple, keeps your code cohesive, and avoids introducing new libraries or patterns.

Option 2: Use an Event Emitter (For More Complex Scenarios)

If you anticipate needing to trigger events across many unrelated components (not just menu closure and user deletion), an event emitter could be a flexible solution. However, it adds a bit of complexity and requires careful cleanup to avoid memory leaks.

First, create an event emitter instance (you can use Node's built-in EventEmitter or a lightweight library like mitt):

// utils/userEventEmitter.ts
import { EventEmitter } from 'events';
export const userEventEmitter = new EventEmitter();

Then, you can either attach it to your Context for easier access or import it directly in components:

// Update your Context type if you want to attach the emitter
type UserModifcationContextType = {
  onEdit?: (objectId: number) => void;
  onDelete?: (objectId: number) => void;
  emitter?: typeof userEventEmitter;
};

// Update the Context default and provider to include it
export const UserModificationContext = createContext<UserModifcationContextType>({
  onDelete: undefined,
  onEdit: undefined,
  emitter: userEventEmitter
});

In your menu parent component, subscribe to the delete event to close the menu:

useEffect(() => {
  const handleUserDeleted = () => setIsMenuOpen(false);
  userEventEmitter.on('userDeleted', handleUserDeleted);
  
  // Always clean up subscriptions to prevent memory leaks
  return () => userEventEmitter.off('userDeleted', handleUserDeleted);
}, []);

When you delete a user, emit the event:

const { onDelete } = useContext(UserModificationContext);

const handleDelete = (userId: number) => {
  onDelete?.(userId);
  userEventEmitter.emit('userDeleted');
};

Caveats:

  • Event emitters make communication implicit, which can make debugging harder compared to explicit prop/Context callbacks.
  • Failing to clean up subscriptions will lead to memory leaks, so always include cleanup logic in useEffect return functions.

Option 3: Lift Menu State to a Higher Level (Most "React-y" Approach)

If your menu's open/close state is tightly tied to user data actions, consider lifting that state up to the same level where you manage your user data. Then, include state setters in your Context so components can update both user data and menu state in one place.

For example:

// In your top-level component managing user data
const [users, setUsers] = useState<User[]>([]);
const [isMenuOpen, setIsMenuOpen] = useState(false);

const handleDeleteUser = (userId: number) => {
  // Update user data
  setUsers(prevUsers => prevUsers.filter(user => user.id !== userId));
  // Automatically close the menu when a user is deleted
  setIsMenuOpen(false);
};

return (
  <UserModificationContextProvider
    onDelete={handleDeleteUser}
    onEdit={handleEditUser}
  >
    {/* Your menu and nested components */}
  </UserModificationContextProvider>
);

This approach keeps all related state and actions co-located, which aligns with React's unidirectional data flow principles.

Recommendation

For your current scenario, Option 1 (Extend Existing Context) is the best choice. It's simple, keeps your code organized, and doesn't introduce unnecessary complexity. If you start to have many cross-component events that don't fit neatly into your existing Context, then consider an event emitter (Option 2). Option 3 is ideal if your menu state is inherently tied to user data actions.

Avoid overcomplicating things with event emitters unless you truly need their flexibility—React's built-in Context and state management tools are usually sufficient for these kinds of use cases.

内容的提问来源于stack exchange,提问作者TheAschr

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 18:33:12