Register Delegation Pattern
Advanced pattern for organizing action handlers in separate modules using external functions and useActionRegister() hook.
Import
typescript
import { createActionContext, ActionPayloadMap, ActionRegister } from '@context-action/react';Features
- ✅ Modular handler organization
- ✅ External function delegation
- ✅ Team-based development support
- ✅ Plugin architecture enablement
- ✅ Cleanup management
Prerequisites
🎯 스펙 재사용: For complete setup instructions including type definitions, context creation, and provider configuration, see Basic Action Setup.
📖 이 문서의 모든 예제는 아래 setup 스펙을 재사용합니다:
- 🎯 Action types → EventActions Pattern
- 🎯 Context creation → Single Domain Context
- 🎯 Provider setup → Single Provider Setup
💡 일관된 학습: Setup 가이드를 먼저 읽으면 이 문서의 모든 예제를 즉시 이해할 수 있습니다.
Overview
Register delegation enables modular handler organization by separating handler logic into external modules. This pattern is essential for large applications with complex business logic, team-based development, and plugin architectures.
Basic Delegation Pattern
tsx
// Framework imports
import { useEffect } from 'react';
import { ActionRegister } from '@context-action/react';
// Setup imports - reusing EventActions from setup guide
import { useEventRegister, EventActions } from '../setup/actions';
// External registration function
function setupAnalyticsHandlers(register: ActionRegister<EventActions>) {
// Register handlers outside of React components
register.register('analytics', async (payload, controller) => {
await analyticsAPI.track(payload.event, payload.data);
}, {
priority: 100,
tags: ['analytics']
});
register.register('userClick', async (payload, controller) => {
await analyticsAPI.trackClick(payload.x, payload.y);
}, {
priority: 90,
tags: ['analytics', 'interaction']
});
}
// Component that delegates registration
function AnalyticsSetup() {
const register = useEventRegister();
useEffect(() => {
if (!register) return;
// Delegate registration to external function
setupAnalyticsHandlers(register);
// Cleanup on unmount
return () => {
register.clearAction('analytics');
register.clearAction('userClick');
};
}, [register]);
return null; // Setup component
}Modular Handler Registration
tsx
// utils/handlers/userHandlers.ts - User domain handlers using UserActions from setup
import { ActionRegister } from '@context-action/react';
import { UserActions } from '../setup/actions';
export function registerUserHandlers(register: ActionRegister<UserActions>) {
register.register('updateProfile', async (payload, controller) => {
try {
await userAPI.updateProfile(payload);
controller.setResult({ success: true });
} catch (error) {
controller.abort('Profile update failed', error);
}
}, { tags: ['user', 'profile'] });
register.register('changePassword', async (payload, controller) => {
const isValid = await validatePassword(payload.currentPassword);
if (!isValid) {
controller.abort('Current password invalid');
return;
}
await userAPI.updatePassword(payload.newPassword);
}, { tags: ['user', 'security'] });
}
// utils/handlers/apiHandlers.ts - API domain handlers using APIActions from setup
import { ActionRegister } from '@context-action/react';
import { APIActions } from '../setup/actions';
export function registerAPIHandlers(register: ActionRegister<APIActions>) {
register.register('fetchData', async (payload, controller) => {
// API data fetching logic
const result = await fetch(payload.endpoint + '?' + new URLSearchParams(payload.params));
const data = await result.json();
controller.setResult(data);
}, { tags: ['api', 'fetch'] });
register.register('uploadFile', async (payload, controller) => {
// File upload logic
const formData = new FormData();
formData.append('file', payload.file);
const result = await fetch('/upload', { method: 'POST', body: formData });
controller.setResult(result);
}, { tags: ['api', 'upload'] });
}
// Component that coordinates multiple handler modules
function AppHandlerSetup() {
const userRegister = useUserRegister();
const apiRegister = useAPIRegister();
const eventRegister = useEventRegister();
useEffect(() => {
if (!userRegister || !apiRegister || !eventRegister) return;
// Register handlers from different modules
registerUserHandlers(userRegister);
registerAPIHandlers(apiRegister);
setupAnalyticsHandlers(eventRegister);
// Cleanup registered handlers on unmount
return () => {
userRegister.clearAction('updateProfile');
userRegister.clearAction('changePassword');
apiRegister.clearAction('fetchData');
apiRegister.clearAction('uploadFile');
eventRegister.clearAction('analytics');
eventRegister.clearAction('userClick');
};
}, [userRegister, apiRegister, eventRegister]);
return null;
}Dynamic Handler Registration
tsx
// Dynamic handler setup based on configuration
import { useStoreValue } from '@context-action/react';
import { useEventRegister } from '../setup/actions';
function DynamicHandlerSetup() {
const register = useEventRegister();
const config = useStoreValue(configStore);
useEffect(() => {
if (!register) return;
// Register handlers based on current configuration
if (config.enableAnalytics) {
setupAnalyticsHandlers(register);
}
if (config.enableNotifications) {
setupNotificationHandlers(register);
}
if (config.debugMode) {
setupDebugHandlers(register);
}
// Return cleanup function
return () => {
register.clearAll();
};
}, [register, config.enableAnalytics, config.enableNotifications, config.debugMode]);
return null;
}Team-Based Handler Organization
tsx
// Team-specific handler modules using setup spec types
// team-auth/handlers.ts
import { ActionRegister } from '@context-action/react';
import { UserActions } from '../setup/actions';
export function registerAuthHandlers(register: ActionRegister<UserActions>) {
register.register('login', authLoginHandler, { tags: ['auth'] });
register.register('logout', authLogoutHandler, { tags: ['auth'] });
// refreshToken handler would need to be added to UserActions in setup
}
// team-products/handlers.ts
import { ActionRegister } from '@context-action/react';
import { APIActions } from '../setup/actions';
export function registerProductHandlers(register: ActionRegister<APIActions>) {
register.register('fetchData', productLoadHandler, { tags: ['products'] });
register.register('postData', productSearchHandler, { tags: ['products'] });
// Additional product handlers would extend APIActions in setup
}
// team-notifications/handlers.ts
import { ActionRegister } from '@context-action/react';
import { NotificationActions } from '../setup/actions';
export function registerNotificationHandlers(register: ActionRegister<NotificationActions>) {
register.register('showNotification', orderNotificationHandler, { tags: ['orders'] });
register.register('hideNotification', paymentNotificationHandler, { tags: ['orders'] });
}
// Main application handler coordination
function TeamHandlerCoordinator() {
const userRegister = useUserRegister();
const apiRegister = useAPIRegister();
const notificationRegister = useNotificationRegister();
useEffect(() => {
if (!userRegister || !apiRegister || !notificationRegister) return;
// Each team registers their handlers using setup specs
registerAuthHandlers(userRegister);
registerProductHandlers(apiRegister);
registerNotificationHandlers(notificationRegister);
return () => {
userRegister.clearAction('login');
userRegister.clearAction('logout');
apiRegister.clearAction('fetchData');
apiRegister.clearAction('postData');
notificationRegister.clearAction('showNotification');
notificationRegister.clearAction('hideNotification');
};
}, [userRegister, apiRegister, notificationRegister]);
return null;
}Best Practices
- Module Organization: Group related handlers in separate modules
- Cleanup Management: Always unregister handlers on unmount
- Type Safety: Pass typed ActionRegister to maintain type safety
- Configuration-Driven: Use config to conditionally register handlers
- Error Handling: Handle registration errors gracefully
- Performance: Register handlers once, not on every render
- Team Boundaries: Organize handlers by team or feature ownership
- Handler IDs: Use descriptive IDs for easier debugging and management
When to Use Register Delegation
- Large Applications: Complex handler logic across multiple modules
- Team Development: Different teams owning different handlers
- Dynamic Configuration: Handlers registered based on runtime config
- Plugin Architecture: Modular handler registration system
- Testing: Easier to mock and test individual handler modules