Image Credits : Udemy

Focus on logic rather than lifecycles : React Hooks

Author: Prateek Rai

Before Justifying above statement lets take a short drive about hooks.

Introduction

REACT HOOKS, first introduced in REACT 16.8, are APIs that allow us to introduce state and other React features in functional components. Within a year from its launch, they took the developer community by a storm. Most of the major libraries including Redux, React-router, Styled-components adopted hooks and are launching their own custom hooks to ease the life of developers. The reason behind the popularity and success of React Hooks are:

  1. Hooks allow to split one complex component into smaller functional components based either on business logic or functionality.
  2. Reusability of the stateful logic in Class based Components in React is achieved through HOC, Render props etc. The issue with this approach is its complexity and its impact on component hierarchy of the app. Contrary, hooks provide an easy alternative to reuse stateful logic without affecting the component hierarchy.
  3. The surveys’ has shown that developers find classes more complex and confusing and various studies have found that classes are also tough on machines.

Hooks can be categorised into two categories:

Basic Hooks

  1. useState
  2. useEffect
  3. useContext

Additional Hooks

Below hooks are either derived from basic hooks, or only needed for specific edge cases.

  1. useReducer
  2. useCallback
  3. useMemo
  4. useRef
  5. useImperativeHandle
  6. useLayoutEffect
  7. useDebugValue

Without going into further detail of every single hook, I will leave the job of exploring them to you, as in my opinion, each of them deserve a blog dedicated to them to explain its working, implementation and use cases. If you are interested in learning about them, I would recommend you to visit the official HOOKS API documentation.

Some rules to use hook

  • They can only be called at Top Level.

Don’t call Hooks inside loops, conditions, or nested functions.

  • They can only be called inside React Functions.

Call Hooks from React function components or custom hooks.

Let’s get back to heading and prove it

Let’s begin with a custom Class Component to fetch user data based on unique user Id and to display the current scroll position.

import React, {Component} from 'react';
class MyComponent extends Component {
constructor(props) {
super(props);
this.state={
currentScroll:0,
}
}
listener =()=>{
const bodyOffset = document.body.getBoundingClientRect();
const scrollY = bodyOffset.top===0?0:-bodyOffset.top;
this.setState({currentScroll:scrollY});
};
componentDidMount() {
const {userID} = this.props;
//Subscribing to Scroll Event Listener
window.addEventListener("scroll", this.listener);
//Data Fetching
fetchDataForUser(userID);
}
componentDidUpdate(prevProps) {
//Data Fetching
const {userID:prevUserID} = prevProps;
const {userID} = this.props;
if(prevUserID !== prevUserID)
{
fetchDataForUser(userID);
}
}
componentWillUnmount() {
//Unsubscribing Event Listener
window.removeEventListener("scroll", this.listener);
}
render() {
const {currentScroll} = this.state;
return (
<div>
Current Scroll Pos : {currentScroll}
</div>
);
}
}

In the above example, we can see:

  1. Unrelated logic bound by lifecycle : Subscription to scroll event happens in componentDidMount lifecycle where we are also perform another unrelated task to fetch user data by network call where as related task of unsubscription is happening in componentWillUnmount lifecycle.
  2. Related logic separated by lifecycle : Now to fetch user data based on user id, we have to think about two life cycles again, i.e. componentDidMount lifecycle to get initial data based on user id and componentDidUpdate lifecycle to get updated user data based on new user id.

The above functionality can also be achieved by using Hooks. Let’s see how Hooks save us from the above complexity & challenges

import React,{useState,useEffect} from 'react';//A Custom Hook to get Scroll Pos which can be reused in any functional component
const useCurrentScrollPos = () => {
const [scrollPos, setScrollPos] = useState(0);
const listener =()=>{
const bodyOffset = document.body.getBoundingClientRect();
const scrollY = bodyOffset.top===0?0:-bodyOffset.top;
setScrollPos(scrollY);
}; useEffect(() => {
//Subscribing to Scroll Event Listener
window.addEventListener("scroll", listener);
return () => {
// Unsubscribing to Event Listener
window.removeEventListener("scroll", listener);
};
}, []);
return scrollPos;
};
const MyComponent =(props)=> {
const scrollPos = useCurrentScrollPos();
const {userId} = props;
//Fetching user data based on current value of userId
useEffect
(()=>{
fetchDataForUser(userID);
},[userId]);
return (
<div>
Current Scroll Pos : {scrollPos}
</div>
);
};

As you can see in the above example:

  1. We can now use state and other React features in functional components.
  2. A custom hook named useCurrentScrollPos has been created which can be plugged into any functional component which promote reuse of state logic without changing component hierarchy. It also prevents us from “wrapper hell” in React DevTools.
  3. The fetching of data for a particular user is now based on business logic rather than lifecycle logic (as in the above case, it is based on userId). This provides us further flexibility.
  4. The hooks based functional components can be distinguished based on the logic they execute. As in the above case, we define the logic of Subscribing/Unsubscribing to scroll events in one hook and the logic to fetch user data separately in other hook.
  5. LOC in hook based components is much lesser than in Class based components.

So, functional components are no more stateles, rather they are more intelligent now than class based components.

Different Hooks we use in Shiksha (Study Abroad)

With the help of hooks, in Shiksha, based on the requirements, we developed some custom hooks. These custom hooks helped us in achieving various benefits (specified in this blog). They also reduce the effort for creating page architecture and made development experience smoother.

Few custom hooks are:

  1. useInfinitePagination : This hook handles infinite scrolling behaviour.
  2. usePageDataHook : This hook manages data fetching tasks for a page.
  3. useTrackingHook: This hook manages tracking related tasks for a page.
  4. useL2OrHeaderStickiness : This hook handles level 2 navigation bar behaviour.
  5. useLazyItem : This hook helps us to lazy load images.

Challenges we faced while adopting hooks in Shiksha

Adopting hooks was not as easy as a pie. As we implemented hooks in our code, we came accross some of problems which I would like to mention in below points:

  • There is a learning curve involved to understand hooks APIs.
  • We have to perform some changes in our SSR architecure to fit this in our system.
  • As experienced React developers, we initially faced a challenge of mind stickiness to React lifecycle as we try to mock lifecycles through Hooks.

Conclusion

Hooks solved an age old issues with React and it is backward compitable too. Because of its advantages and simplicity it has already taken us by storm same as React community. Within few months of its adoption we already have made a lot of custom hooks helping us to share logic, ease development etc. Hooks shifted our mindset from writing class based components to functional components. Now, we are gradually migrating our class based components to functional components based on hooks.