Understanding Higher-Order Components (HOCs) in React with a Real-World Example

Higher-Order Components (HOCs) in React can seem like a complex concept, especially if you’re not deeply embedded in the React ecosystem. However, they are a powerful tool for managing component logic and reusability. In this article, I’ll try to demystify HOCs and illustrate their usage with a relatable real-world example.

What is a Higher-Order Component?

In simple terms, a Higher-Order Component (HOC) is a function that takes a component and returns a new component with added functionality. It’s a pattern used to share common logic between multiple components without repeating code.

Think of HOCs as decorators in a coffee shop. You have a basic coffee, and you can enhance it by adding milk, sugar, or flavors, making it a cappuccino, latte, or vanilla coffee. The basic coffee remains the same, but the enhancements (HOCs) provide additional features.

Real-World Example: Access Control in a Web Application

Imagine a web application where certain pages are restricted to users with specific roles, such as admin or manager. We need a way to enforce this access control across various components without duplicating the logic. This is where HOCs come in handy.

Step-by-Step Implementation

  • Basic Component

First, let’s create a basic component that displays a dashboard.

import React from 'react';

function Dashboard() {
  return <div>Welcome to Admin Dashboard</div>;
}

export default Dashboard;
  • HOC for Access Control

Next, we’ll create an HOC that checks if the user has the required role to view the component.

import React from 'react';

function withAuthorization(WrappedComponent, allowedRoles) {
  return function(props) {
    const { user } = props;

    if (allowedRoles.includes(user.role)) {
      return <WrappedComponent {...props} />;
    } else {
      return <div>Access Denied</div>;
    }
  };
}

export default withAuthorization;

This ‘withAuthorization ‘HOC takes two arguments: the ‘WrappedComponent‘ (the component to be enhanced) and ‘allowedRoles‘ (an array of roles permitted to view the component). It returns a new component that either renders the WrappedComponent or displays an “Access Denied” message based on the user’s role.

  • Using the HOC

Now, let’s use the ‘withAuthorization‘ HOC to protect the ‘Dashboard‘ component.

import React from 'react';
import Dashboard from './Dashboard';
import withAuthorization from './withAuthorization';

const user = { role: 'admin' }; // Example user object

const AuthorizedDashboard = withAuthorization(Dashboard, ['admin', 'manager']);

function App() {
  return (
    <div>
      <AuthorizedDashboard user={user} />
    </div>
  );
}

export default App;

In this example, we create an ‘AuthorizedDashboard‘ by wrapping the Dashboard component with the ‘withAuthorization‘ HOC. We specify that only users with the role of ‘admin’ or ‘manager’ can access this component.

  • Rendering the Application

When the App component is rendered, the ‘AuthorizedDashboard‘ will check the user’s role. If the user’s role is included in the allowed roles, the Dashboard will be displayed. Otherwise, an “Access Denied” message will appear.

Benefits of Using HOCs

  1. Code Reusability: HOCs allow you to encapsulate reusable logic in a single place. This makes your code more modular and maintainable.
  2. Separation of Concerns: HOCs help separate the logic of enhancing components from the components themselves. This keeps components focused on their primary purpose: rendering UI.
  3. Consistency: By using HOCs, you ensure consistent behavior across your application. For instance, access control logic implemented in an HOC will be consistently applied to all components that use it.

Conclusion

Higher-Order Components (HOCs) are a powerful pattern in React for enhancing components with reusable logic. By abstracting common functionality, such as access control, into HOCs, you can keep your code DRY (Don’t Repeat Yourself) and maintainable.

In this real-world example, it’s demonstrated how to use an HOC to manage access control in a web application. This pattern can be extended to various scenarios, such as logging, error handling, and more.

Understanding and leveraging HOCs can significantly improve your React development process, leading to cleaner, more efficient, and scalable code.

ReactJS Class Components vs Functional Components – a quick tour

Recently my team was tasked with a project that was developed in ReactJS to be modernized and made more performance optimized. The project was developed using a mix of class components and functional components, without any state management nor any performance considerations for large amounts of data payloads from server. We decided to move the project from class components to functional components, add redux for state management and bring in several performance optimizations. When working with the project I realized that the original development was done without understanding subtle difference and similarities between class components and functional components as well as about the usage of life cycle hooks.

Hence, I thought of sharing some key and fundamental difference between the two so that this could be used as a reference in the future.

  • Component Definition

Functional Components

function MyComponent(props) {
  return <div>Hello, {props.name}!</div>;
}

Class Components

import React, { Component } from 'react';

class MyComponent extends Component {
  render() {
    return <div>Hello, {this.props.name}!</div>;
  }
}
  • State Management

Functional Component with ‘useState

import React, { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

Class Component with ‘this.state

import React, { Component } from 'react';

class Counter extends Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }

  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>
          Click me
        </button>
      </div>
    );
  }
}
  • Lifecycle Methods

Functional Component with ‘useEffect

import React, { useEffect } from 'react';

function Timer() {
  useEffect(() => {
    const timer = setInterval(() => {
      console.log('Tick');
    }, 1000);

    return () => clearInterval(timer);
  }, []);

  return <div>Check the console</div>;
}

Class Component Lifecycle Methods

import React, { Component } from 'react';

class Timer extends Component {
  componentDidMount() {
    this.timer = setInterval(() => {
      console.log('Tick');
    }, 1000);
  }

  componentWillUnmount() {
    clearInterval(this.timer);
  }

  render() {
    return <div>Check the console</div>;
  }
}
  • Handling Events

Functional Component

import React from 'react';

function ClickHandler() {
  const handleClick = () => {
    console.log('Button clicked');
  };

  return <button onClick={handleClick}>Click me</button>;
}

Class Component

import React, { Component } from 'react';

class ClickHandler extends Component {
  handleClick = () => {
    console.log('Button clicked');
  };

  render() {
    return <button onClick={this.handleClick}>Click me</button>;
  }
}
  • Props and State Comparison

Functional Component

function Greeting(props) {
  return <h1>Hello, {props.name}</h1>;
}

function ParentComponent() {
  const [name, setName] = useState('World');

  return (
    <div>
      <Greeting name={name} />
      <button onClick={() => setName('React')}>Change Name</button>
    </div>
  );
}

Class Component

import React, { Component } from 'react';

class Greeting extends Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

class ParentComponent extends Component {
  constructor(props) {
    super(props);
    this.state = { name: 'World' };
  }

  render() {
    return (
      <div>
        <Greeting name={this.state.name} />
        <button onClick={() => this.setState({ name: 'React' })}>Change Name</button>
      </div>
    );
  }
}
  • Context API

Functional Component with ‘useContext

import React, { createContext, useContext } from 'react';

const MyContext = createContext();

function ChildComponent() {
  const value = useContext(MyContext);
  return <div>{value}</div>;
}

function ParentComponent() {
  return (
    <MyContext.Provider value="Hello from Context">
      <ChildComponent />
    </MyContext.Provider>
  );
}

Class Component with ‘Context.Consumer

import React, { Component, createContext } from 'react';

const MyContext = createContext();

class ChildComponent extends Component {
  render() {
    return (
      <MyContext.Consumer>
        {value => <div>{value}</div>}
      </MyContext.Consumer>
    );
  }
}

class ParentComponent extends Component {
  render() {
    return (
      <MyContext.Provider value="Hello from Context">
        <ChildComponent />
      </MyContext.Provider>
    );
  }
}
  • Higher-Order Components (HOCs)

Functional Component (HOC)

import React from 'react';

function withLogging(WrappedComponent) {
  return function(props) {
    console.log('Component rendered with props:', props);
    return <WrappedComponent {...props} />;
  };
}

function MyComponent(props) {
  return <div>{props.message}</div>;
}

const MyComponentWithLogging = withLogging(MyComponent);

Class Component (HOC)

import React, { Component } from 'react';

function withLogging(WrappedComponent) {
  return class extends Component {
    componentDidMount() {
      console.log('Component rendered with props:', this.props);
    }

    render() {
      return <WrappedComponent {...this.props} />;
    }
  };
}

class MyComponent extends Component {
  render() {
    return <div>{this.props.message}</div>;
  }
}

const MyComponentWithLogging = withLogging(MyComponent);

Conclusion

  • State Management: Use useState in functional components and this.state in class components.
  • Side Effects: Use useEffect in functional components and lifecycle methods (componentDidMount, componentWillUnmount, etc.) in class components.
  • Event Handling: Both use similar syntax, but class components use this to refer to class methods.
  • Context API: Use useContext in functional components and Context.Consumer in class components.
  • HOCs: Higher-Order Components work similarly in both, but syntax differs slightly.

In conclusion, understanding the above key differences and similarities will help to navigate and work between functional and class components effectively.

Software Engineering – Merge Sort

I recently had to a do an application that required me to sort a data set fast and efficiently. I opted to do a merge sort. Merge sort is a fast sorting algorithm which is always true with an average or worst case performance of O(n log n). What I really love about a merge sort is that it is a stable sorting algorithm.

After doing the sorting I realized that most people are unaware of how actually merge sort worked. Hence I decided to do this post so anyone can actually understand how merge sort worked behind the scene and give example codes using java and C of a merge sort on an integer array.

Merge Sort is a divide an concur sorting algorithm. The Array is divided repeatedly until we have only 2 arrays of only one element each. Then the elements in the two arrays are sorted and we work our way back up the hierarchy merging the arrays till we get a sorted array. You can get a very good description on the wiki site about merge sort.

If we take an array of 6 integers, say 5,12,3,3,9,18, we follow the process below

  1. Break the array in to two arrays -> 5,12,3 and 3,9,18
  2. Take each array and break it again -> 5 and 12,3
  3. Break the array till you have only 1 element each -> 12 and 3
  4. Now start sorting and merging – >3 and 12
    1. one layer up 3,12 and 1 -> 1,3,12
    2. one layer up 1,3,12 and 3,9,18 – > 1,3,3,9,12,18
  5. Now you have a sorted array

Let me explain this now with a Java code.
Note that I did not use any specific java syntax such as array.length to keep the code as generic as possible. The code methods are self explanatory.

class merge_sort
{

    public int[] split_merge(int [] mainArr , int start, int end)
    {
	if ((end - start) < 2) {
	    return mainArr;
	}

	int middle = (start + end) / 2;

	int[] A = new int[middle - start];
	int[] B = new int[end - middle];

	A = copy_array(mainArr, start, middle,A);
	B = copy_array(mainArr, middle, end ,B);

        //Recursively Call split_merge till we have an 
        //array with 1 element
	A = split_merge(A,0,middle);
	B = split_merge(B,0 ,(end-middle));
        
        //merge our way back one layer up always.
	mainArr = merge(mainArr, A, B, start, middle, end);
	return mainArr;
    }

    public int[] merge(int[] array, int[] A, int[] B, int start, int middle, int end)
    {
	int i = 0 , j = 0, k = 0;
        //Loop till end of main array and copy the sorted elements.
	for (k=0;k<(end-start);k++){
	    if ( (i < middle)  && (A[i] < B[j]) ){
		    array[k] = A[i];
		    i=i+1;
	    }else{
		    array[k] = B[j];
		    j=j+1;
	   }
	}

	return array;
	
    }

    public int[] copy_array(int[] A, int start, int end, int[] B)
    {
	int i =start, j =0;
        while (i < end) {
	    B[j] = A[i];
	    j+=1;
	    i+=1;
	}
	return B;
    }
    public  static void  main(String args[])
    {
	int mainArr[] = {2,3,5,1,6,4,10};
	int length = 7;

	for(int i =0 ; i <mainarr.length; i++){
="" system.out.format("unsorted="" array[%d]="%d" \n="" ",="" i="" ,="" mainarr[i]);="" }="" mainarr="new" merge_sort().split_merge(mainarr,0,length);="" for(int="" ;="" <mainarr.length;="" system.out.format("sorted="" <="" pre="">

Now let’s look at the same as implemented using C language. Again I have tried to make it language agnostic as possible.

#include <stdio.h>
#include <string.h>

int *merge_sort(int A[],int length);
int *copy_array(int *A,int start , int end , int *B);
int *split_merge(int *A, int start, int end);
int *merge(int *arr, int *A, int *B, int start,int middle, int end);


int* copy_array(int *A, int start, int end,int *B)
{
 int i , j;
  j = 0;
  for ( i = start; i< end; i++){
    B[j] = A[i];
    j = j +1;
  }
  return B;
}

int *merge(int *arr , int *A, int *B, int start, int middle, int end)
{
  int i = 0;
  int j = 0;
  int k = 0;

  for(k = 0; k < end ; k++){
    if((i < middle) && (A[i] < B[j])){
      arr[k] = A[i];
      i = i +1 ;
    }else {
      arr[k] = B[j];
      j = j +1;
    }  
  }
  return arr;
}

int *split_merge(int *arr, int start, int end)
{
  if((end-start) < 2)
    return arr;
  int k;
  
  int middle = (end -start) /2;
  int size_a = middle - start;
  int size_b = end - middle;
  
  int A[size_a];
  int B[size_b];
  int *_aptr = copy_array(arr , 0 , middle , A);
  int *_bptr = copy_array(arr, middle , end, B);

  _aptr = split_merge(_aptr,0,middle);
  _bptr = split_merge(_bptr,0, (end - middle));

  arr = merge(arr , _aptr , _bptr , start , middle, end);
  return arr;
  
}

int *merge_sort(int A[], int length)
{
  int start  = 0;
  int end = length; //I put this to match the Java Code
  A = split_merge(A,start,end);
  return A;
}


int main(int argc, char argv[])
{
  int A[] = {100,50,200,1000,18,5300};
  int length = 6;
  int *B;

  for ( i = 0;i < length ; i++){
    printf("Unsorted Array[%d] : %d \n",i , A[i]);
  }

  int *aptr = merge_sort(A , length );
  int i;
 
  for ( i = 0;i < length ; i++){
    printf("Sorted Array[%d] : %d \n",i , aptr[i]);
  }
  
  return 0;
}

So I hope that this will help you next time when you have to sort a data set and you will choose merge as an option to your application.

Connecting RabbitMQ with PIKA for 10000 EPS

Hey all , I know it has been a long time since I posted any articles on my site due to and extremely busy schedule. But I wanted to start again giving out my experience so that someone can benefit from that.

In the last week or so I have been tasked to create an event driven architecture to handle 10000+ EPS (events per second). The platform I chose  was to use message queue that will act as the main bus for all messages to be streams. For this purpose I have used RabbitMQ and pika (as the system is written in Python). One of the key challenges was to create an asynchronous publisher which had to be thread safe for 10000+ EPS. Searching over the net I could not find any good resource for this but found several supporting articles that helped me to build this out.

Following is a simple python pika publisher to publish messages asynchronously and in a thread safe environment.

import logging
import pika
import json
import time
from logging.handlers import RotatingFileHandler
from threading import Thread



class Publisher(Thread):

    def __init__(self, RABBITMQ_SETTINGS,LOG4PY_SETTINGS):
        Thread.__init__(self)       
        self.logger = logging.getLogger("Publisher.py")
        self.connection = None
        self.channel = None
        self._deliveries = []
        self._acked = 0
        self._nacked = 0
        self._message_number = 0
        self._stopping = False
        self.queue = 'alienvault_replicate'
        self.routing_key = 'alienvault_replicate'
        self.exchange = 'alienvault_replicate'
        self.message = None
        self.ready = False
        self._closing = False
        log4py_file  = LOG4PY_SETTINGS['log4py_file']
        log4py_log_level = LOG4PY_SETTINGS['log4py_log_level']
        self.PUBLISH_INTERVAL=0.1
        self.RABBITMQ_SETTINGS =RABBITMQ_SETTINGS

        if log4py_log_level == 'DEBUG':
           self.log_level = logging.DEBUG
        elif log4py_log_level == 'INFO': 
           self.log_level = logging.INFO
        elif log4py_log_level == 'WARN':
           self.log_level = logging.WARN
        elif log4py_log_level == 'ERROR': 
           self.log_level = logging.ERROR

        self.logger.setLevel(self.log_level)
        # create console handler and set level to debug
        rfh = RotatingFileHandler(filename=log4py_file, mode='a', maxBytes=100*1024*1024,backupCount=2)
        rfh.setLevel(self.log_level)
        # create formatter
        formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    
        # add formatter to ch
        rfh.setFormatter(formatter)
    
        # add ch to logger
        self.logger.addHandler(rfh)
        
        amqp_url = 'amqp://'+self.RABBITMQ_SETTINGS['user']+':'+self.RABBITMQ_SETTINGS['passwd']+'@'+self.RABBITMQ_SETTINGS['host']+':'+str('5672')+'/%2F'
        self._url = amqp_url
        
    def is_ready(self):
        return self.ready
        
    def set_message(self,message):
        self.message = message
        self.logger.info('Message set to publish to {0}'.format(self.message))
    
    
    def connect(self):
        self.logger.info('Connecting to %s', self._url)
        return pika.SelectConnection(pika.URLParameters(self._url),
                                     self.onconnection_open)
        
    def close_connection(self):
        self.logger.info('Closing connection')
        self._closing = True
        self.connection.close()

    def add_onconnection_close_callback(self):
        self.logger.info('Adding connection close callback')
        self.connection.add_on_close_callback(self.onconnection_closed)

    def onconnection_closed(self, connection, reply_code, reply_text):

        self.channel = None
        if self._closing:
            self.connection.ioloop.stop()
        else:
            self.logger.warning('Connection closed, reopening in 5 seconds: (%s) %s',
                           reply_code, reply_text)
            self.ready = False                   
            self.reconnect()


    def onconnection_open(self, unusedconnection):
        self.logger.info('Connection opened')
        self.add_onconnection_close_callback()
        self.openchannel()

    def reconnect(self):
        self.connection.ioloop.stop()
        self.connection = self.connect()
        self.connection.ioloop.start()

    def add_onchannel_close_callback(self):
        self.logger.info('Adding channel close callback')
        self.channel.add_on_close_callback(self.onchannel_closed)

    def onchannel_closed(self, channel, reply_code, reply_text):
        self.logger.warning('Channel was closed: (%s) %s', reply_code, reply_text)
        if not self._closing:
            self.connection.close()

    def onchannel_open(self, channel):
        self.logger.info('Channel opened')
        self.channel = channel
        self.add_onchannel_close_callback()
        self.setup_exchange(self.exchange)

    def setup_exchange(self, exchange_name):
        self.logger.info('Declaring exchange %s', exchange_name)
        self.channel.exchange_declare(self.on_exchange_declareok,
                                       exchange_name)

    def on_exchange_declareok(self, unused_frame):
        self.logger.info('Exchange declared')
        self.setup_queue(self.queue)

    def setup_queue(self, queue_name):
        self.logger.info('Declaring queue %s', queue_name)
        self.channel.queue_declare(self.on_queue_declareok, queue_name,durable=True)

    def on_queue_declareok(self, method_frame):
        self.logger.info('Binding %s to %s with %s',
                    self.exchange, self.queue, self.routing_key)
        self.channel.queue_bind(self.on_bindok, self.queue,
                                 self.exchange, self.routing_key)


    def publish_message(self):
        if self._stopping:
            return
        if self.message == None:
            return

        try:
            properties = pika.BasicProperties(delivery_mode = 1)
    
            self.channel.basic_publish(self.exchange, self.routing_key,
                                        self.message,
                                        properties)
            self.logger.info('Published message # %i', self._message_number)
        except Exception as err:
            import trace
            self.logger.info("Error in sending message ... {0}".format(err.message))
            self.ready = False

    def start_publishing(self):
        self.logger.info('Issuing consumer related RPC commands')
        self.ready = True
        self.publish_message()

    def on_bindok(self, unused_frame):
        self.logger.info('Queue bound')
        self.start_publishing()

    def closechannel(self):
        self.logger.info('Closing the channel')
        if self.channel:
            self.channel.close()

    def openchannel(self):
        self.logger.info('Creating a new channel')
        self.connection.channel(on_open_callback=self.onchannel_open)

    def run(self):
        self.connection = self.connect()
        self.connection.ioloop.start()


    def stop(self):
        self.logger.info('Stopping')
        self._stopping = True
        self.closechannel()
        self.close_connection()
        self.connection.ioloop.start()
        self.logger.info('Stopped')

if __name__ == '__main__':
    try:
        RABBITMQ_SETTINGS = {"user":"user","passwd":"pw","host":"xxx.xxx.xxx.xxx"}
        LOG4PY_SETTINGS = {"log4py_file":"./log4py.log","log4py_log_level":"INFO"}
        
        publisher = Publisher(RABBITMQ_SETTINGS,LOG4PY_SETTINGS)
        publisher.start()
        for i in range(1 , 100000):
            message = '{"message":"Hello Fellasss...."'+str(i)+'}'
            publisher.set_message(message)
            while not publisher.is_ready():
                time.sleep(.001)
            publisher.publish_message()
        publisher.stop()    
    except KeyboardInterrupt:
        publisher.stop()

What is the cost of going Agile

Recently I was interested in a user group question about what is the actual cost of implementing Agile within an organization. I believe that using the term “cost” when it comes to Agile implementations may not be the right term and I would rather prefer “ROI”. However following is my take on what is the cost in implementing Agile within an organization.

From what I seen in Agile implementations I have been involved in and coached, the cost is mainly going to be in the buy in from all to adopt to Agile. What I have seen is moving an organization to be Agile is a cultural change rather then just doing Agile (which is some teams doing something like scrum or XP or kanban etc…).

The idea is as a whole, the organization becomes agile by having constant improving itself from the way they interact with clients to the development and everything in between. On paper everyone will say ‘YES’ let’s go agile, but when things get tough, people will always try to find some way of breaking the process. So their has to be commitment from all areas. This can not be commanded, it has to be accepted and adopted.

So simply we need to ask our self and our management, are we willing to really get in to being agile, then the cost comes down to ROI. That is the whole investment including

  • Learning curve (investment of teaching employees about Agile and agile framework of choice)
  • Slowdown in progress initially (impacts business and clients)
  • Getting customer buy in
  • Investment on tools (getting the right tools for the right tasks)
  • Investment on coaching (getting a coach if needed)
  • Monitoring and Improving every step of the way (there is an investment for making sure Agile sticks when the going get’s tough as well)

Note that Agile does not bring ROI immediately. It will be a slow progressing journey, which at the end will bring very high returns.