The chain of responsibility pattern is one of those essentials which can solve the headaches in building an authentication system in react.(or similar)
it’s a Behavioral design pattern which encapsulates the logic for a request, passing on that request along each handler or item till it’s ignored or handled as intended.
So how is this applied in React.
Say we would like to create an authentication system in react which checks
- If a user is an admin or regular
- checks if a users subscription has expired
- Redirects or shows the appropriate selection.
We can make use of an if/else statement or violate the single responsibility principle but as more requirements arise that logic becomes messy. plus… we can move handlers around to handle requests before the next eg. check subscription before user type, remove if needed with minimal effort. ie, Reduced coupling!
Implementation
- create an interface and contract for handling requests
interface IHandler {
setNext(handler: Handler): void; // Can be named differently + (optional) - it passes the request to the next handler.
handleRequest(request: any): void;
}
Implement Base handler class which is common to all
- Implement base handler which implements logic common to all classes which would use the CoR pattern
abstract class AbstractHandler implements IGenericResponsibility {
private _nextHandler: IGenericResponsibility | null = null;
public addNextHandler(handler: IGenericResponsibility): IGenericResponsibility {
this._nextHandler = handler;
return handler;
}
public Handle(args: any): any { // Any can be replaced with your type as needed
if (this._nextHandler !== null) {
return this._nextHandler.Handle(args);
} else {
return null;
}
}
}
- create a class which makes use of the abstract class above
// base class to handle the common request
class checkAdmin extends AbstractHandler {
public Handle(args: any): any {
// Handle the request here, or pass it to the next handler
// if this handler cannot handle it.
if (/* condition to handle the request */) {
return /* Result from handling the request */;
} else {
return super.Handle(args); // Pass the request to the next handler
}
}
}
// we do the same above for all handlers extending the abstract or base handler.
class checkSubscription extends AbstractHandler {
public Handle(args: any): any {
// Handle the request here, or pass it to the next handler
// if this handler cannot handle it.
if (/* condition to handle the request */) {
return /* Result from handling the request */;
} else {
return super.Handle(args); // Pass the request to the next handler
}
}
}
- initialize class which makes use of the concrete handler.
const checkAdmin = new checkAdmin();
const checkSubscription = new checkSubscription();
checkSubscription.addNextHandler(checkAdmin); //if we have more handlers we keep stacking.
//Then we trigger the handler by calling the method at which we would like to start from.
concreteHandler.handle(request):
- Create hook which makes use of the handler
import axios (or preferred) from "axios" or "preferred"
const useCOResponsibility = async ()=>{
const checkAdmin = new checkAdmin();
const checkSubscription = new checkSubscription();
checkSubscription.addNextHandler(checkAdmin); //if we have more handlers we keep stacking.
//Then we trigger the handler by calling the method at which we would like to start from.
const result = concreteHandler.handle(request):
if(result === "condition")
{
//perform condition.
}
}
Making use of a hook should not hinder us as well, as we can also make use of the concrete handler directly within our component ie.
const FuntionalComponent = ()=>{
const checkAdmin = new checkAdmin();
const checkSubscription = new checkSubscription();
checkSubscription.addNextHandler(checkAdmin); //if we have more handlers we keep stacking.
//Then we trigger the handler by calling the method at which we would like to start from.
const result = concreteHandler.handle(request):
useEffect (()=>{
if(result === "condition")
{
//perform condition.
}
},[result]) // or a similar logic to handle a result
return <JSX/>
}
}
That’s it.
At the moment, the CoR is one of my favorite design patterns compared to others, such as the strategy pattern. This is because the flow of my logic moves in a sequential and logical manner with the CoR pattern, and it allows me to call one item before the other, switch out places, and apply the single responsibility and open/closed principles with less complexity. These factors result in clear and concise functionality in my code.
What do you think/which is your pattern and why do you make use of it.? Feel free to send your response as a message or comment below.
Resources
- Wikipedia - Chain-of-responsibility pattern
- https://refactoring.guru/design-patterns/chain-of-responsibility
- https://www.javatpoint.com/behavioral-design-patterns
External Links
- Twitter: @benny_wayn
- LinkedIn: Ibenge-uforo
- Ongoing Research: Social Survey
Top comments (0)