Three essential react hooks  that will supercharge your next webapp.
Tags: react, javascript
Published: 2021-08-20 12:05:03

Let's be honest, we all want to become better react developers. We all have been there where hacking together a todo app or a weather app made us feel like a bad-ass. It's time to move ahead and learn few advanced react hooks that will arm us to build more cool and complex webapps.

Take a deep dive and understand the practical usage of these three advanced React Hooks.

* useEffect
* useReducer
* useContext

## useEffect Hook

To better understand useEffect, we need to be aware of two important things-

### What is React's main job?
 
React's main job is to render the UI, react to user's interaction with the UI and then again re-render the UI based on the type of interaction. This is only what react as a library cares about i.e to bring the UI on the screen.

### What are side-effects ?

All such tasks/operations happening inside our react component that are not directly related to bringing UI on the screen but are absolutely necessary in certain cases(example - to populate the UI with data, we need to fetch data from an external API source) to produce a end-result(or, UI) can be regarded as side-effects. 

Operations like -
 * Sending HTTP requests to the remote server and handling potential errors. All modern web apps do this, we do not need specifically need react's mechanism for that.
 * Storing data in browser's native storage. 
 * Setting and managing timers, etc.

All these tasks must happen outside of the normal component evaluation and render-cycle, especially since these tasks may delay/block rendering of the UI.

We use useEffect hook to handle such side-effects related code, in simpler terms this hook creates a portal for such potentially performance-intensive code, so that this block of code will be executed outside the normal component evalutation and will not hinder the rendering of UI.

useEffect hook takes 2 arguments -
 * An anonymous function that holds the side-effect.
 * A dependency array that allows us to gain control over the hook and let us determine when the hook should be called.

#### Syntax

```javascript
import { useEffect } from "react";

function App() {

 useEffect(()=> {
   //side-effect code
 }, [x, y]) // x, y are placeholders for state/props/methods.
 
 return (
   // JSX
 );
};
```

#### Things to keep in mind when using the useEffect hook -
* This anonymous arrow function that holds the side-effect code  is executed only after evaluation of the component has completed. 
* If the dependency array is an empty array [ ], the useEffect will run only once after the initial mounting of the component.
* useEffect is triggered when one of the dependencies gets updated. The dependencies can be a state, props.The useEffect will run again after react has finished the component evalaution because one of the items in dependency array was updated in last component render/evaluation cycle.
* useEffect is triggered when the component unmounts the DOM.

#### A word of caution 
```javascript
import { useEffect } from "react";

function App() {

 useEffect(()=> {
   //side-effect code
 }, ) // No dependency array
 
 return (
   // JSX
 );
};
```

is same as 

```javascript
function App() {
 
 //side-effect code
//this code will be executed everytime the component is re evaluated
 
 return ( 
   // JSX
 );
};
```

We should never leave the 2nd argument unmentioned, otherwise we won't be able to leverage the advantage of useEffect hook. 

#### Some RULES that will help deciding what should we mention inside dependency array :

- Do not to add "built-in" APIs or functions like fetch(), localStorage etc (functions and features built-into the browser and hence available globally). These browser APIs / global functions are not related to the react component render cycle and they also never change.
- Do not add variables or functions we might've defined outside of our component (e.g. helper function in a separate file) Such functions or variables are not created inside of a component function and hence changing them won't affect your components.
- Do not add state updating functions, react guarantees that those functions never change, hence we don't need to add them as dependencies(you could though).


#### useEffect Cleanup

There are two types of side-effects : one requires cleanup another does not. Also, The type of operation done in useEffect might be a problem. If it was performance intensive code then it might slow down the app, if we would be sending HTTP request to check if login details are correct, our current useEffect would
send a lot of uncessary requests, that will lead to network traffic. 

##### Syntax

```javascript
import { useEffect } from "react";

function App() {

 useEffect(()=> {
   //side-effect code
   
  return () => {}

 }, [x, y]) // x, y are placeholders for state/props/methods.
 
 return (
   // JSX
 );
};
```

NOTE:
1. When the useEffect runs for the very first time i.e when component mounts the DOM, cleanup doesnot run.
2. Now for every useEffect execution after the point (1), cleanup will run first and then only sideeffect code runs.
3. Cleanup runs when the component unmounts the DOM.

#### When to use useEffect 
* Control a piece of code.
* Avoid potential infinite loop because we are triggering a state update inside our component.
* It also helps avoiding running the performance intensive code along with every component re evaluation.

## useReducer() Hook
This is another built in React Hook. It Helps in state management.
This hook is to be used to manage complex state in react components. It adds more structure to the state and also combine all methods that cause a state update at one place.
It accepts a reducer of type `(state, action) => newState`, and returns the current state paired with a dispatch method. (If you’re familiar with Redux, you already know how this works.)

We can import useReducer from react just like the other React hooks, as seen in the following snippet:

```javascript
import React, { useReducer } from 'react';
const [state, dispatch] = useReducer(reducer, initialState);
```

This hook function returns an array with 2 values. The first one is the state value, and the second value is the dispatch function which is further used to trigger an action with the help of array destructuring.  

useReducer is usually preferable to useState when you have complex state logic that involves multiple sub-values or when the next state depends on the previous one. useReducer also lets you optimize performance for components that trigger deep updates because you can pass dispatch down instead of callbacks.

Here, is an example to better understand the useReducer hook.

```javascript
// reducer function
const initialState = {count: 0};

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
    </>
  );
}
```

## useContext

useContext hook allows you to easily share app wide or component wide state and state updating methods easily.

But, we need to understand what was the need of useContext hook in the first place?

### What is Prop Drilling?
 When we need to pass down state or methods from a component sitting at the top of component tree hierarchy to the component sitting low at the hierarchy, and in this traversal we have to pass them props even to those component which dont necesarrily need it, they act merely as carrier components. As the component tree starts getting bigger, it becomes more complex to pass down props and methods to every component, this is where useContext hook comes for the rescue.

create a context first: 

AuthContext.js
```javascript

import { createContext } from "react";

// Create context object
// Takes a default context in argument.
// Context -> App  or component wide state.

// AuthContext is an object that contains a component
const AuthContext = createContext ( { 
	isLoggedIn : false,
} );

export default AuthContext;

```

To use context in the app, we need to do things -
1. Provide it = all the components that are wrapped by it should have access to it.
2. Consume it -> Hook into it -> Listen to it 

App.js ->
```javascript

import AuthContext from './store/auth-context';

function App () {
  // Something
   return ( 
     // AuthContext is just an object, whose .Provider proprty is 
        a Component
    <AuthContext.Provider>
      // JSX
     // root component here
    // Auth-Context provider is a component we can use in our JSX code, and we can wrap it around other components and those other components and all their descendant components. So all their children and their children's children and so on all those components will now have access to that Context.
    </AuthContext.Provider>
  );
}

```
we can listen in two ways. We can listen by using Auth-Context consumer or by using a React Hook (Recommended way).

This hook allows you to tap into the context and use it.

```javascript
import React, { useContext } from 'react';

import AuthContext from './AuthContext.js';

const Navbar = (props) => {
  const ctx = useContext(AuthContext);

  return (
    <nav className={classes.nav}>
      <ul>
        {ctx.isLoggedIn && (
          <li>
            <a href="/">Users</a>
          </li>
        )}
      </ul>
     </nav>
)
```


### Conclusion

These three advanced react hooks allows you to build more powerful and robust frontend web applications. The vast usage of react hooks have made modern way of building web apps easier.

Happy Coding!