React is a great front-end development library, which is working with the speed of light and is easy to pick up and start to work with it. It utilizes simple concepts and mainly uses common JavaScript knowledge to build up single page applications, instead of creating exotic abstractions on top of the well-known front-end layer. React works with components based architecture to build up the UI. Your application will be a component, which can contain other components nested into each other.
Frameworks, like Angular or Vue enforce a structure on your code, with React you are not bound to folders or best practices by the library. This means, that if you do not act carefully, you can end up with a very deeply nested, codependent component graph, which will be very hard to unit test, let alone maintain.
There are some interesting ideas based on great experience on how to separate different types of logic into different types of containers (here). This solves the issue of coupling the data fetching logic and the data presentation logic. The main idea behind most of these approaches is to make parts of the application code independent and small, in order to prevent too high complexity.
The problem what I faced
I count myself a reasonably experienced full stack web developer, who initially started with the Microsoft based stack, but since then I've broadened my repertoire. Despite that I learn React for only like 20 days I have seen similar problems in other domains several times.
To learn React, I've started to get as much information about it as I can. I've started to listen to podcasts, read discussions and even skimmed a book. After I thought I had what I needed to fuel my curiosity, I started to build a project to cut my teeth into real problems. My application is a news portal with articles all over the place.
The problem that resulted in this post was about one of my components which was meant to display article headlines and metadata about the article. One article can have three different state in my application:
- An article can be invisible - filtered out from presentation, with the searching
- An article can be still loading - and for practice I've decided to put skeleton articles in the place of the ones still not loaded.
- And finally and article can be fully presented on the app.
Let's see some simplified code example for this:
import React from 'react';
class Article extends React.Component {
constructor(props) {
super(props);
this.state = { articles : [] };
}
async componentDidMount() {
const result = await fetch('http://sample.com/');
const articles = await result.json();
this.setState({articles: articles});
}
render() {
return this.state.articles.map( article => {
if (!article.visible) return <React.Fragment />;
else if (article.loading) {
return <div className="article skeleton" />;
}
else {
return (
<div className="article">
{article.title}
</div>);
}
});
}
}
export default Article;
Of course the skeleton and the fully rendered article were a little more complex than this dummy example above, overall the render method for this component was more then 100 lines! A lot of lines means a higher complexity than what I like to deal with at once.
The pattern arrives to save the day...
As I saw this, I started to formulate the idea, that maybe it is time to use the Chain of Responsibility pattern to make the component understandable for a glance. As RefactoringGuru states:
Chain of Responsibility is a behavioral design pattern that lets you pass requests along a chain of handlers. Upon receiving a request, each handler decides either to process the request or to pass it to the next handler in the chain.
This really seems like something we could use here to simplify this complex render function. Imagine to have the following handlers:
- Invisible article handler - if the article is not visible it renders an empty fragment, else it passes the request along.
- Loading article handler - if the article is loading, it renders the skeleton, else it passes the request along.
- Full article handler - renders the full article.
So, we need to create these handlers and a way to chain them one after another. Consider the following UML diagram from Wikipedia, to understand how the implementation works:
The Handler will keep a list of all handlers in order, which will try to process the incoming request from the Sender (in our case the sender is the render method). Once Reciever1 gets the request it decides whether it can handle it or not. If not, then it will call back to the Handler to pass the request to the next handler (receiver on the picture). This goes on until one receiver actually handles the request.
In the implementation I wanted to use as many features of ES6, as I could - so I can learn more out of this.
The solution
Let's create first the actual handlers, which will process the requests, and later on focus on the mechanism to chain them together.
First write a handler to handle the request if the article is invisible, if not, just call back to the parent object to handle the request.
import React from 'react';
class InvisibleArticleHandler extends ArticleHandler {
handleRequest = article => {
if (!article.visible) {
return <React.Fragment />;
}
return super.handleRequest(article);
}
}
export default InvisibleArticleHandler;
Next, write a handler to handle the request if the article is loading, if not, just call back to the parent object to handle the request.
import React from 'react';
class LoadingArticleHandler extends ArticleHandler {
handleRequest = article => {
if (article.loading) {
return <div className="article skeleton" />;
}
return super.handleRequest(article);
}
}
export default LoadingArticleHandler;
Last, write a handler to handle the request if the article is fully loaded.
import React from 'react';
class FullArticleHandler extends ArticleHandler {
handleRequest = article => (
<div className="article">
{article.title}
</div>
);
}
export default FullArticleHandler;
Now it is time to write the parent class, that is extended in the concrete handlers. This class is keeping tack of the handlers.
class ArcticleHandler {
constructor() {
this.handlers = [];
}
addHandler = handler => { this.handlers.push(handler); }
empty = () => { this.handlers = []; }
handleRequest(arcticle) {
// FIFO - get the first handler from the array of handlers.
const nextHandler = this.handlers.shift();
// Pass the list of handlers to the concrete reciever object,
// as when it is calling into it's parent's method, the call
// is on that object, not on the original handler!
nextHandler.handlers = this.handlers;
return nextHandler.handleRequest(arcticle);
}
}
export default ArcticleHandler;
Using this, we can end up with a lot more readable article component to present the news:
import React from 'react';
import ArcticleHandler from './ArcticleHandler';
import InvisibleArticleHandler from './InvisibleArticleHandler';
import LoadingArticleHandler from './LoadingArticleHandler';
import FullArticleHandler from './FullArticleHandler';
class Article extends React.Component {
constructor(props) {
super(props);
this.state = { articles : [] };
}
async componentDidMount() {
const result = await fetch('http://sample.com/');
const articles = await result.json();
this.setState({articles: articles});
}
render() {
const articleHandler = new ArticleHandler();
return this.state.articles.map( article => {
// Reset the handlers for each article
articleHandler.empty();
articleHandler.addHandler(new InvisibleArticleHandler());
articleHandler.addHandler(new LoadingArticleHandler());
articleHandler.addHandler(new FullArticleHandler());
return arcticleHandler.handleRequest(arcticle);
});
}
}
export default Article;
With utilizing the Chain of Responsibily pattern the render method can be written with a domain specific language, so next time you come by this method you will see, that it will try to render an article based on rules in the described order.
I really hope that I provided some value to you with giving you the insight I got from tackling a complexity related issue. I will continue to post here in the future my next findings from my journey to learn React properly.
Any comments / feedback is more than welcome, either here or on my Twitter @xavios5 !
Top comments (9)
I like the explanation and demonstration of the Chain of Responsibility pattern and I appreciate the clear, concise article. However; I have a couple of questions.
Please ignore the second question if the purpose of this article is to make readers aware of the pattern and demonstrate a possible implementation.
Hi Philip,
Thank you so much for taking the time to read the post, and also thanks for the kind feedback. Let me answer your questions:
1) The Artcile component in this example becomes a client of the ArtcileHandler, so Article uses a service, which is provided by the ArticleHandle. Article gives a request to the ArticleHandler, and asks it to retrive a proper JSX element for that answer. ArticleHandler does this by holding a list of different handlers and passing the request to them one-by-one. Eventually one handler will be able to serve the response, and it can be consumed by the Article component.
As you see, within this context the role of the Article component and and ArticleHandler are different, so it would not make sense to extend one form an other.
So instead of endless switch cases and if statements we usually turn to this pattern. The benefit: we can see in the code easily against what the request will be validated, with an unambiguous domain specific language.
A fronted example: consider that you would like to render a table which consist information about the shares an investor firm owns in different trading companies.
Fidelity Capital Investment Partners Ltd
Code | Name | Amount
COKE | Coca Cola Ltd | 10%
PEPSI | Pepsi Int Ltd | 100.000 $
NIKE | Nike Sport | 120 BPI
So let's see the rules for displaying the amount:
As you can see writing this logic to display a single cell in the grid would take quite a few swithces and / or if-s. In this case I do think it is worthwhile the mental overhead to future-proof our code.
Hope I made some sense :)
-Xavios
Thanks for your reply! I can see using the Chain of Responsibility pattern for complex situations like the examples you've provided. Thanks again for your article π
Sounds like a very complicated way to achieve something very simple.
I'd suggest you move away from the oop/class approach and try a more functional approach.
Hi guico33,
Thanks for taking the time to read the post, and also thanks for the feedback! Considering the above example, what would be the correct functional way to approach that (investor data table cell logic) problem? I really would like to know, and next time utilize it!
Cheers,
Xavios
Not that much to be done given the first code snippet is already very straightforward.
You may create a stateless
Article
component that will take care of rendering one article.Instead of classes you can use function components (with hooks to manage state and side effects).
You could encapsulate the fetch logic into a custom hook but that's already overthinking it imo.
You will find that the community is alienating with OOP, at least when it comes to the react framework.
hmmm... Nice writeup but when I look at the original file content:
I wonder a little about the need, I wonder if you over-simplified your example to not overload the article.
The first call to
render
will render an empty array asthis.state.articles
is equal to[]
. Soarticle.loading
will never be used.Then, when the fetch is done, you have a list of articles assigned to
this.state.articles
. Before doing that, if you don't display invisible article, I would have filtered the values returned by the server. (by the way, why does the server even return those articles.)Resulting class would be: