Skip to main content

Migration guide: v0.11.x → v0.12.0

This guide will help you migrate your codebase from use-less-react v0.11.x to v0.12.0. The main changes focus on improving the Developer Experience (DX) and type safety of the Event Bus.

Overview of Changes

The v0.12.0 release introduces significant improvements to the Event Bus:

  1. New EventHandlerBuilder class for fluent handler registration
  2. Improved type inference in registerLocalHandler
  3. API simplification with removal of unregisterLocalHandler

Breaking Changes

1.Improved Type Inference for registerLocalHandler

The type signature of registerLocalHandler has been improved to provide better developer experience.

Before (v0.11.x)

// specify type AND payload
bus.registerLocalHandler<"UserCreated", UserCreatedPayload>(
"UserCreated",
async (event: UserCreatedEvent) => {
console.log('User created:', event.payload.email);
}
);

After (v0.12.0)

Pass event class as generic type: type and payload are inferred:

const unsubscribe = bus.registerLocalHandler<UserCreatedEvent>(
"UserCreated",
async (event: UserCreatedEvent) => {
console.log('User created:', event.payload.email);
}
);

Or, if your handler function is explicitly typed:

const handler: LocalEventHandler<UserCreatedEvent> = async (event) => {
console.log('User created:', event.payload.email);
}

// no need to specify any generics
const unsubscribe = bus.registerLocalHandler(
"UserCreated",
handler
);

2. Removal of unregisterLocalHandler

The unregisterLocalHandler method has been removed from the Event Bus API.

Use the cleanup function returned by registerLocalHandler and by EventHandlerBuilder.build.


New Features

EventHandlerBuilder

The new EventHandlerBuilder class provides a fluent API for registering multiple event handlers. This is particularly useful when you need to register many handlers and want a centralized cleanup mechanism.

Quick Example

import { EventHandlerBuilder } from '@dxbox/use-less-react/classes';

const cleanup = new EventHandlerBuilder(bus)
.on<UserCreatedEvent>("UserCreated", handler1)
.on<UserCreatedEvent>("UserCreated", handler2)
.on<UserDeletedEvent>("UserDeleted", handler3)
.build();

// Cleanup all handlers at once
cleanup();

When to Use

Recommended when:

  • You need to register 2 or more handlers
  • You want to group registrations logically
  • You need centralized cleanup (e.g., in React useEffect)

Not necessary when:

  • Registering a single handler
  • Handlers have different lifecycles

For more details, see the EventHandlerBuilder documentation.


Migration Checklist

Use this checklist to ensure a smooth migration:

  • Search for unregisterLocalHandler: Find all occurrences in your codebase

  • Update to unsubscribe pattern: Replace with the function returned by registerLocalHandler

  • Update registerLocalHandler generics: Change from <TType, TPayload> to <EventClass> wherever the handler is not explicitly typed. Otherwise simply omit the generics.

  • Consider using EventHandlerBuilder: For multiple handler registrations

  • Update tests: Ensure your tests reflect the new API

  • Update TypeScript types: If you have type definitions that reference the old API

  • Run type checker: Verify everything compiles

    npm run type-check
    # or
    tsc --noEmit
  • Run tests: Ensure all tests pass

    npm test

Common Migration Patterns

Pattern 1: Simple Handler Registration

// Before (v0.11.x)
bus.registerLocalHandler<"UserCreated", UserCreatedPayload>(
"UserCreated",
async (event) => {
console.log(event.payload.email);
}
);

bus.unregisterLocalHandler("UserCreated", handler);

// After (v0.12.0)
const unsubscribe = bus.registerLocalHandler<UserCreatedEvent>(
"UserCreated",
async (event) => {
console.log(event.payload.email);
}
);

unsubscribe();

Pattern 2: Multiple Handlers in a Service

// Before (v0.11.x)
class UserService {
private handler1 = async (event: UserCreatedEvent) => { /* ... */ };
private handler2 = async (event: UserDeletedEvent) => { /* ... */ };

initialize() {
bus.registerLocalHandler<"UserCreated", UserCreatedPayload>(
"UserCreated",
this.handler1
);
bus.registerLocalHandler<"UserDeleted", UserDeletedPayload>(
"UserDeleted",
this.handler2
);
}

destroy() {
bus.unregisterLocalHandler("UserCreated", this.handler1);
bus.unregisterLocalHandler("UserDeleted", this.handler2);
}
}

// After (v0.12.0) - Option 1: Manual cleanup
class UserService {
private unsubscribers: Array<() => void> = [];

initialize() {
this.unsubscribers.push(
bus.registerLocalHandler<UserCreatedEvent>(
"UserCreated",
async (event) => { /* ... */ }
)
);
this.unsubscribers.push(
bus.registerLocalHandler<UserDeletedEvent>(
"UserDeleted",
async (event) => { /* ... */ }
)
);
}

destroy() {
this.unsubscribers.forEach(unsub => unsub());
this.unsubscribers = [];
}
}

// After (v0.12.0) - Option 2: Using EventHandlerBuilder (Recommended)
class UserService {
private cleanup?: () => void;

initialize() {
this.cleanup = new EventHandlerBuilder(bus)
.on<UserCreatedEvent>("UserCreated", async (event) => { /* ... */ })
.on<UserDeletedEvent>("UserDeleted", async (event) => { /* ... */ })
.build();
}

destroy() {
this.cleanup?.();
}
}

Need Help?

If you encounter issues during migration:

  1. Check the Event Bus documentation
  2. Open an issue on GitHub

Version Compatibility

  • v0.12.0 and later: Use the new API as described in this guide
  • v0.11.x and earlier: Use the old API with unregisterLocalHandler
  • Mixed versions: Not recommended. Update all packages to v0.12.0 or later

Summary

The v0.12.0 migration primarily involves:

  1. ✅ Replacing unregisterLocalHandler calls with unsubscribe functions
  2. ✅ Updating registerLocalHandler generic parameters to use event classes
  3. ✅ Optionally adopting EventHandlerBuilder for cleaner code

These changes result in:

  • 🎯 Better type safety
  • 🚀 Improved developer experience
  • 📚 More maintainable code
  • 💡 Better IDE support

Happy coding! 🎉