DEV Community

Cover image for Simpler React component design with the Chain of Responsibility pattern
Xavios
Xavios

Posted on

Simpler React component design with the Chain of Responsibility pattern

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;
Enter fullscreen mode Exit fullscreen mode

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:

  1. Invisible article handler - if the article is not visible it renders an empty fragment, else it passes the request along.
  2. Loading article handler - if the article is loading, it renders the skeleton, else it passes the request along.
  3. 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:
Chain of Responsibility React JS - JSXThe 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;
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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 !

Oldest comments (9)

Collapse
 
walkhard13 profile image
Phillip Smith

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.

  1. Why do we not have Article extend AticleHandler?
  2. Do you have an example of a project where the benefit of using this pattern would outweigh the additional overhead (cognitive, testing, maintenance, future refactoring) of implementing this pattern?

Please ignore the second question if the purpose of this article is to make readers aware of the pattern and demonstrate a possible implementation.

Collapse
 
xavios5 profile image
Xavios

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.

  1. In my day job we create software for institutional banks. There I came across quite a few examples of this, but mostly in back-end parts. These were mostly about validating business rules, like let's assume that there is a service to download investor related data in PDF form:
  • is the user authenticated?
  • does the user have authorization to reach the content?
  • is the requested investor data proprietary?
  • is the format allowed for pdf generation?
  • for all parts check whether we need to generate it?
  • etc..

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:

  • if the firm is not in the USA display percentage
    • display red background if the percentage went down since yesterday
    • display yellow background if the percentage is stable
    • display green if it went up
  • if the firm is in the USA, but the percentage is less then 1%, then display basis point indexes, where 1000 BPI = 1%
    • if the BPI wend down display it with italics, else in bold
  • if the firm is from abroad display the dollar value
    • if the dollar value is greater than 1 million $ then display it whit 1.XX M $ format

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

Collapse
 
walkhard13 profile image
Phillip Smith

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 😁

Collapse
 
guico33 profile image
guico33

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.

Collapse
 
xavios5 profile image
Xavios

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

Collapse
 
guico33 profile image
guico33

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.

Collapse
 
chaseholdren profile image
Chase Holdren
import React from "react";

class ArticleList 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 });
  }

  renderArticle = article => {
    if (!article.visible) return <React.Fragment />;

    if (article.loading) return <div className="article skeleton" />;

    return <div className="article">{article.title}</div>;
  };

  render() {
    return this.state.articles.map(renderArticle);
  }
}

export default ArticleList;
Enter fullscreen mode Exit fullscreen mode
Collapse
 
wnemay profile image
Wayne May πŸ‡ΏπŸ‡¦πŸ‡ΊπŸ‡Έ

You will find that the community is alienating with OOP, at least when it comes to the react framework.

Collapse
 
callmemagnus profile image
magnus

hmmm... Nice writeup but when I look at the original file content:

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;

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 as this.state.articles is equal to []. So article.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:

class Article extends React.Component {

  constructor(props) {
    super(props);
    this.state = { articles : null };
  }

  async componentDidMount() {
    const result = await fetch('http://sample.com/');
    const articles = await result.json();
    this.setState({
      articles: articles.filter(article => !!article.visible)
    });
  }

  render() {
    if (!this.state.articles) {
      return <div className="articles-skeleton-list" />
    }
    return this.state.articles.map(article => (
      <div className="article">
        {article.title}
      </div> 
    ));
  }
}
export default Article;