- Object State instead of multiple parameter state
When we have a form with multiple input fields such as first name, last name, email, password etc. developer tends to create useState variable for each field which looks like below code.

However, the developer can create a common object which will have all the form fields. Moreover, we can use only one common function to set the values of each field and even add any validation required.

-
Handling of non-primitive type
Whenever we are using a non-primitive type of data, we need to be a bit more cautious with its usage. As observed in the code below, we have a form object which has two parameters in it i.e., name and age. In handleClick function we are calling the setForm with same object. So apparently there is no change in the object and ideally it should not re-render the page. But we can see that the log inside useEffect gets executed.

The reason why it gets logged is that Object is a non-primitive type. Whenever we call the setForm method it will update the form variable with new object (even if key and value remain same), and the useEffect gets called.
Another way to understand this is given below:

Evidently two variables of primitive type having same value, when gets compared then it returns true. But when two object of same key and value are compared it returns false.

If we are using form.name it will watch only the name variable updates. As a result, we do not see the console getting executed as shown in below screenshot.

-
. Unnecessary usage of useState
Suppose we have a simple login page with Username and Password. To design this, most often developer tends to make use of useSate as shown below.

Whenever the user changes username or password field, the component will get re-rendered.

To avoid this unnecessary rendering, developers should have proper understanding of when to have useState defined in the component. In this scenario, developer should make use of useRef hooks and access the value using useRef.current.value as shown below.


The React documentation clearly specifies list of situations where effects may not be required and how it can be replaced.
-
Stale closure
Often developers tend to write extremely good code but forget to do one of the most important aspects i.e., cleanup process. Consider the below example where we have a button Click me which is associated with the useRef hook. On click of this button, we are just executing a function which is responsible for just logging the output. The issue with this code is that we are not cleaning up the event listener, which means that it will remain intact even after the component is unmounted.


It is a good practice to do the cleanup process to avoid any memory leakage and boost the performance of your application. We can return a function inside useEffect which will be responsible for calling the removeEventListener function.


-
Infinite loop
Hooks are common in React ecosystem but requires time to master it. New developer configures the useEffect in such a way that it causes infinite loop. Below are few common scenarios where we observe this:
a) No dependency array for useEffect

We can fix this just by passing no dependencies in the dependency array as shown below.

b) Passed incorrect dependency.
If the developer passes count as dependency inside useEffect, it causes to re-render the page again. The count will keep on incrementing and hence causing an infinite loop.

We can simply pass no dependency in the useEffect which eventually prevents the infinite loop.

c) Improper use of event handlers.
Consider a scenario where developer wants to increment the count variable by using onClick function as shown below.

As the button gets rendered, it will get the setCount executed which will set the count variable by 1. This will render the component which causes the setCount method called again. As a result, we observe an infinite loop for the above scenario as well.
This can be fixed by just passing a function to the onClick event handler and not the setCount result directly.
