Why do you need the React Hooks cheat sheet? In the React world, the introduction to Hooks was a welcome addition by the developer community. Essentially,
Hooks are functions that let you “hook into” React state and lifecycle features from functional components.
They are backward-compatible and don’t work with classes. If you are someone who is new to the world of Hooks or even a seasoned pro, this cheat sheet will help you to get all of your doubts and queries clear.
Basic level - React Hooks Cheat Sheet
1. Why were Hooks introduced, or why do we need them?
The React team introduced Hooks for the following reasons:
- 
To reuse stateful logic without changing the component hierarchy: with Hooks, we can extract stateful logic from a component so that it can be tested independently and reused when needed. This makes it easy to share Hooks among many components or with someone else on the team. 
Patterns like render props and Higher Order Components (HOCs) require you to restructure your components. This can be cumbersome and makes the code harder to follow. Sharing stateful logic became better after Hooks were introduced. 
- 
To split one component into smaller functions: Hooks have also made coding lifecycle methods easier than ever. Each of its lifecycle methods often contains a mix of unrelated logic. Components might perform data fetching using 
componentDidMount(), on the other hand, this method might include some unrelated logic that sets up event listeners. 
In many cases, breaking these components into smaller functions is impossible as the stateful logic is all over the place from one lifecycle method to another. For instance, if we need to fetch user data using componentDidMount() in a class component, we would do something like this:
import React, { Component } from 'react'
import Axios from 'axios'
export default class Hello extends Component {
    constructor(props) {
        super(props);
        this.state = { username: " "};
    }
    componentDidMount() {
        Axios.get('/my-api/user/username')
        .then(response => {
            this.setState({ name: response.data.username })
        })
    }
    render() {
        return (
            <p>
                Username is {this.state.username}
            </p>
        )
    }
}
While if we want to do the same with the built-in React Hooks like useState and useEffect we will do it like:
import React, { useEffect, useState } from 'react'
import Axios from 'axios'
export default function Hello() {
    const [username, setUsername] = useState(" ")
    useEffect(() => {
        Axios.get('/my-api/user/username')
        .then(response => {
            setUsername(response.data.username)
        })
    }, [])
    return (
        <p>
            Username is {username}
        </p>
    )
}
The code written using these two Hooks is more straightforward and concise. Moreover, it's easy to understand and work on if we work in a team. Using lifecycle methods like componentDidMount(), componentDidUpdate(), and componentWillUnmount(), they are handled individually, but when using React Hooks, you can handle all of this with just the useEffect() Hook.
- To use more React features without classes: prior to when there was no concept of Hooks in React, its components used mainly class-based components. Making your components with classes all the time required great (and sometimes unnecessary) effort because you always had to switch between the classes, render props, HOCs, etc. Thanks to React Hooks, now you can do all of these without switching using functional components.
 
Take a look at the two different code snippets where we need to display the classic "Hello, world!" in a div container for the Hello() component:
- Using class-based component:
 
import React, { Component } from 'react'
export default class Hello extends Component {
    render() {
        return(
            <div>
            Hello World!
            </div>
        )
    }
}
- Using functional component:
 
import React from 'react'
export default function Hello() {
    return (
        <div>
            Hello World!
        </div>
    )
}
If you compare both of these, the second approach to using a functional component is a significantly simpler code that gets us the same result. You don't need to allocate space to a class instance and then call the render() function. Instead, you simply call the function.
2. The useState Hook
The
useStateHook lets you use the local app state within a functional component.
It returns a stateful value along with a function to update it. Here's its basic call signature:
const [state, setState] = useState(initialState);
The setState function is used to update the state, and it accepts a new state value (newState):
setState(newState);
Here are some of the crucial points regarding useState:
- 
Declaring a state variable: to declare a state variable, you just need to call 
useStatewith some initial value:() => { const [count] = useState(100) return <div> State variable is {count}</div> } 
- 
Updating a state variable: here, you need to invoke the update function returned by invoking 
useState: 
() => {
  const [items, setItems] = useState(0)
  const handleClick = () => setItems(items + 1)
  return (
    <p>
      The user has {items} number of items.
      <p>
        <button onClick={handleClick}>Increase number of items by 1</button>
      </div>
    </div>
  )
}
- 
Using multi-state variables: you can use multiple state variables and update them within a functional component, as shown in this example:
() => { const [items, setItems] = useState(0) const [price, setPrice] = useState(10) const handleItems = () => setItems(items + 1) const handlePrice = () => setPrice(price + 10) return ( <div> <p>The user has {items} number of items.</p> <p>And they sell items worth {price} USD.</p> <div> <button onClick={handleItems}> Increase number of items by 1 </button> <button onClick={handlePrice}> Increase price by 10 </button> </div> </div> ) } 
- 
Using object state variables: you can also use an entire object as an initial value passed to 
useState. It will not automatically merge update objects.Let's say our initial state is `{ name: "John" }`, then if we use `setState` to add `age`: setState({ age: 'unknown' });The new state object will become.
{ name: "John", age: "unknown" }. - 
Functional 
useState: the updater function returned after invokinguseStatecan also take a function just like in what we used to do in class-based component'ssetState:const [value, updateValue] = useState(0) // Either this updateValue(1); // Or this updateValue(previousValue => previousValue + 1);Both these forms are valid for invoking
updateValue. 
3. The useEffect Hook
The
useEffectHook accepts a function that is used to perform any side effects.
The function passed to useEffect will run after the render is committed to the screen. These effects run after the render is complete, but we can also choose to fire them based on changes in certain values.
Here's the call signature of useEffect:
useEffect(effectFunction, arrayDependencies)
To make a basic side effect, we will be using both useState and useEffect Hooks in the following example:
() => {
  const [age, setAge] = useState(0)
  const handleClick = () => setAge(age + 1)
  useEffect(() => {
    document.title = 'You are ' + age + ' years old!'
  })
  return <div>
    <p> Look at the title of the current tab in your browser </p>
    <button onClick={handleClick}>Update Title</button>
  </div>
}
The useEffect block updates the current tab/browser window's title after running the handleClick function.
The following points are helpful regarding useEffect:
- 
Cleaning up an effect: we must clean up the effect after a period, and this is usually done by returning a function from within the effect function passed to 
useEffect.The following example shows the usage of `useEffect` to create and clean a subscription within an app: useEffect(() => { const subscription = props.source.subscribe(); return () => { // Cleaning up the effect subscription.unsubscribe(); }; });This cleanup function is run to prevent memory leaks before the component is removed from the UI. Also, if we write a new cleanup, the previous effect is cleaned up before executing the next one.
 
- 
Creating multiple effects: to add multiple effects, we can have more than one 
useEffectcalls within a functional component, as shown:() => { // First effect useEffect(() => { const clicked = () => console.log('window clicked') window.addEventListener('click', clicked) return () => { window.removeEventListener('click', clicked) } }, []) // Second effect useEffect(() => { console.log("another useEffect call"); }) return <div> Check your console logs </div> } 
- 
Multiple ways to skip effects: we can skip 
useEffectcalls not to invoke it on every render. This can be done in multiple ways:
- 
Using an array dependency: here, `useEffect` is passed an array of a value. With this, the effect function will be called while mounting *and* whenever that value we passed is generated. Here's an example:
() => { const [randomNumber, setRandomNumber] = useState(0) const [effectLogs, setEffectLogs] = useState([]) // Passing in `randomNumber` in the array dependency useEffect( () => { setEffectLogs(prevEffectLogs => [...prevEffectLogs, 'effect fn has been invoked']) }, [randomNumber] ) return ( <div> <h1>{randomNumber}</h1> <button onClick={() => { setRandomNumber(Math.random()) }} > Generate random number! </button> <div> {effectLogs.map((effect, index) => ( <div key={index}>{'🍔'.repeat(index) + effect}</div> ))} </div> </div> ) } 
- 
Using an empty array dependency: in this case, `useEffect` is passed an empty array `[]`. The effect function is now called only while mounting:
() => { const [randomNumber, setRandomNumber] = useState(0) const [effectLogs, setEffectLogs] = useState([]) // Passing an empty array `[]` here useEffect( () => { setEffectLogs(prevEffectLogs => [...prevEffectLogs, 'effect fn has been invoked']) }, [] ) return ( <div> <h1>{randomNumber}</h1> <button onClick={() => { setRandomNumber(Math.random()) }} > Generate random number! </button> <div> {effectLogs.map((effect, index) => ( <div key={index}>{'🍔'.repeat(index) + effect}</div> ))} </div> </div> ) } 
- 
Using no array dependency: we can entirely skip effects without providing any array dependency. The effect function will be run after every single render:
useEffect(() => { console.log("This will be logged after every render!") }) 
 - 
Using an array dependency: here, `useEffect` is passed an array of a value. With this, the effect function will be called while mounting *and* whenever that value we passed is generated. Here's an example:
 
4. Rules of Hooks
To effectively use React Hooks, we need to follow two basic rules when using them:
- 
Call Hooks at the Top Level: this rule states that:
Don’t call Hooks inside loops, conditions, or nested functions.
This means you are ensuring that Hooks are called in the same order each time a component renders. This is helpful in preserving your app's state between multiple
useStateanduseEffectcalls.So you should always make sure to use Hooks at the top level of your function before any early returns. React relies on the order in which Hooks are called, so if we have Hook calls in the same order every time, it looks like this:
// First render useState('Mary') // 1. Initialize the name state variable with 'Mary' useEffect(persistForm) // 2. Add an effect for persisting the form useState('Poppins') // 3. Initialize the surname state variable with 'Poppins' useEffect(updateTitle) // 4. Add an effect for updating the title // Second render useState('Mary') // 1. Read the name state variable (argument is ignored) useEffect(persistForm) // 2. Replace the effect for persisting the form useState('Poppins') // 3. Read the surname state variable (argument is ignored) useEffect(updateTitle) // 4. Replace the effect for updating the titleBut if we put a Hook call of
persistForminside a condition:if (name !== '') { useEffect(function persistForm() { localStorage.setItem('formData', name); }); }Then we are breaking the first rule by using a Hook inside a condition! Now the order changes as follows:
useState('Mary') // #1 Read the name state variable (argument is ignored) // useEffect(persistForm) // This Hook was skipped! useState('Poppins') // #2 (but was #3). Fail to read the surname state variable useEffect(updateTitle) // #3 (but was #4). Fail to replace the effectReact doesn’t know what to return for the second
useStatecall. Hence multiple Hooks were skipped causing issues. 
- 
Only Call Hooks from React Functions: this rule states that:
Don’t call Hooks from regular JavaScript functions.
By following this rule, we ensure that all stateful logic code of a component is clearly visible from its source code.
Instead of calling them from regular functions, we can:
- Call Hooks from React function components.
 
- Call Hooks from custom Hooks.
 
 
Intermediate level - React Hooks Cheat Sheet
1. Building custom Hooks
Apart from using the provided useState and useEffect Hooks, we can make a custom Hook as per the need to extract component logic into reusable functions.
Custom Hooks are some functions whose name starts with "use," which may call other Hooks.
It doesn't need to have a specific signature. The following points should be kept in mind when making a custom Hook:
- 
Extracting a custom Hook: custom Hooks are used when we want to share logic between two JavaScript functions and then extract it to a third function. We should make sure to only call other Hooks unconditionally at the top level of the custom Hook. 
Here's an example:import { useState, useEffect } from 'react'; // Our custom Hook function useFriendStatus(friendID) { const [isOnline, setIsOnline] = useState(null); useEffect(() => { function handleStatusChange(status) { setIsOnline(status.isOnline); } ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange); }; }); return isOnline; }In this example, we see that our
useFriendStatusHook uses theuseStateanduseEffectHook to function along with some custom code to subscribe to a friend's status. 
- 
Using a custom Hook: after making a custom Hook, we can use it anywhere inside our components. For example, if we have the following custom Hook called 
useBoolean, which returns the state and the functions to update the state in an array:const useBoolean = (initialState = false) => { const [state, setState] = React.useState(initialState); const handleTrue = () => setState(true); const handleFalse = () => setState(false); const handleToggle = () => setState(!state); return [ state, { setTrue: handleTrue, setFalse: handleFalse, setToggle: handleToggle, }, ]; };Note that we are passing the
initialStateas an argument along with its default value (false). Back in theAppcomponent, we can make use of this Hook by passing an initial state to it and by using its returned values to display the state and update it, as shown below:function App() { const [isToggle, { setToggle }] = useBoolean(false); return ( <div> <button type="button" onClick={setToggle}> Toggle </button> {isToggle.toString()} </div> ); } 
2. The useContext Hook
The useContext Hook saves you from the stress of having to rely on a Context consumer.
It accepts a context object and returns the current context value for that context.
The context object comes from React.createContext, and it's determined by the value prop of the nearest <MyContext.Provider> above the calling component in the tree.
Here's its call signature:
const contextValue = useContext(contextObject)
React's Context is initialized with the createContext top-level API. Here's an example:
import React from 'react';
const CurrencyContext = React.createContext(null);
export { CurrencyContext };
The createContext function takes an initial value which is also the default value if no value prop is defined. We can use it in our example Book component as shown:
const Book = ({ item }) => {
  return (
    <CurrencyContext.Consumer>
      {(currency) => (
        <li>
          {item.title} - {item.price} {currency}
        </li>
      )}
    </CurrencyContext.Consumer>
  );
};
The useState Hook takes the Context as a parameter to retrieve the value from it. Now, if we want to write the above Book component from useContext we end up with the following:
const Book = ({ item }) => {
  const currency = React.useContext(CurrencyContext);
  return (
    <li>
      {item.title} - {item.price} {currency}
    </li>
  );
};
3. The useReducer Hook
The
useReducerHook is an alternative touseState. It is usually preferable when you have a complex state logic that involves multiple sub-values or when the state depends on the previous one.
It has the following call signature:
const [state, dispatch] = useReducer(reducer, initialArg, init);
It accepts a reducer of type (state, action) => newState and returns the current state paired with a dispatch method. Here's an example where this Hook is used:
const initialState = { width: 15 }
const reducer = (state, action) => {
  switch (action) {
    case 'plus':
      return { width: state.width + 15 }
    case 'minus':
      return { width: Math.max(state.width - 15, 2) }
    default:
      throw new Error("what's going on?" )
  }
}
const Bar = () => {
  // `useReducer` call
  const [state, dispatch] = useReducer(reducer, initialState)
  return <>
    <div style={{ background: 'teal', height: '30px', width: state.width }}></div>
    <div style={{marginTop: '3rem'}}>
        <button onClick={() => dispatch('plus')}>Increase bar size</button>
        <button onClick={() => dispatch('minus')}>Decrease bar size</button>
    </div>
    </>
}
render(Bar)
We can initialize the state lazily by passing an init function as its third argument. Whatever is returned from this function is returned as the state object. Here's an example to illustrate this:
// Adding a new function
const initializeState = () => ({
  width: 100
})
const initialState = { width: 15 }
const reducer = (state, action) => {
  switch (action) {
    case 'plus':
      return { width: state.width + 15 }
    case 'minus':
      return { width: Math.max(state.width - 15, 2) }
    default:
      throw new Error("what's going on?" )
  }
}
const Bar = () => {
  // Passing `initializeState` as third argument
  const [state, dispatch] = useReducer(reducer, initialState, initializeState)
  return <>
    <div style={{ background: 'teal', height: '30px', width: state.width }}></div>
    <div style={{marginTop: '3rem'}}>
        <button onClick={() => dispatch('plus')}>Increase bar size</button>
        <button onClick={() => dispatch('minus')}>Decrease bar size</button>
    </div>
    </>
}
render(Bar)
4. The useCallback Hook
The
useCallbackHook is used to return a memoized version of the passed callback that only changes if one of the dependencies has changed.
It's useful when passing callbacks to optimized child components to prevent unnecessary renders. Here's its call signature:
const memoizedCallback = useCallback(function, arrayDependency)
To use useCallback with an inline function, we can call it as follows:
const App = () => {
  const [age, setAge] = useState(99)
  const handleClick = () => setAge(age + 1)
  const someValue = "someValue"
  return (
    <div>
      <Age age={age} handleClick={handleClick} />
      // Inline call to `useCallback`
      <Instructions doSomething={useCallback(() => {
        return someValue
      }, [someValue])} />
    </div>
  )
}
Here, the <Instructions /> child component has the doSomething prop, which is passed with a call to the useCallback Hook.
5. The useMemo Hook
The
useMemoHook allows memoizing expensive functions to avoid calling them on every render.
You pass a "create" function and an array of dependencies, and it will spit out its memoized value. This Hook has the following call signature:
const memoizedValue = useMemo(compute, dependencies)
The following points should be kept in mind when using the useMemo Hook:
- It will only recompute the memoized value when one of the dependencies you pass has changed.
 - We should not add side effects to its call; they should instead belong to the 
useEffectHook. - During the initial rendering, 
useMemoinvokescompute, memoizes the result, and returns to its component. - If we don't provide any array, a new value will be computed on every render of the component where 
useMemois used. 
Let's say we have a <CalculateFactorial /> component:
import { useState } from 'react';
export function CalculateFactorial() {
 const [number, setNumber] = useState(1);
 const [inc, setInc] = useState(0);
 const factorial = factorialOf(number);
 const onChange = event => {
   setNumber(Number(event.target.value));
 };
 const onClick = () => setInc(i => i + 1);
 return (
   <div>
     Factorial of
     <input type="number" value={number} onChange={onChange} />
     is {factorial}
     <button onClick={onClick}>Re-render</button>
   </div>
 );
}
function factorialOf(n) {
 console.log('factorialOf(n) called!');
 return n <= 0 ? 1 : n * factorialOf(n - 1);
}
We can memoize the factorial calculation here whenever the component re-renders by using useMemo(() => factorialOf(number), [number]) as shown in the updated code below:
...
export function CalculateFactorial() {
 const [number, setNumber] = useState(1);
 const [inc, setInc] = useState(0);
// `useMemo` usage
const factorial = useMemo(() => factorialOf(number), [number]);
 const onChange = event => {
   setNumber(Number(event.target.value));
 };
 const onClick = () => setInc(i => i + 1);
 return (
   <div>
     Factorial of
     <input type="number" value={number} onChange={onChange} />
     is {factorial}
     <button onClick={onClick}>Re-render</button>
   </div>
 );
}
function factorialOf(n) {
 console.log('factorialOf(n) called!');
 return n <= 0 ? 1 : n * factorialOf(n - 1);
}
6. The useRef Hook
The
useRefHook returns a mutablerefobject whose.currentproperty is initialized to the passed argument(initialValue).
It has the following call signature:
const refContainer = useRef(initialValue);
The following points are essential about useRef:
- 
useRefacts as a "box" that can hold a mutable value in its.currentproperty. - This Hook creates a plain JavaScript object. The difference between 
useRef()and creating a{ current: ... }object yourself is thatuseRefgives you the samerefobject on every render. - If you mutate the 
.currentproperty, it will not cause a re-render. The Hook doesn't notify you when its content changes. - The value of the reference is persisted, i.e., it stays the same between component re-renderings.
 - References can also access DOM elements. For that, we need to refer to the 
refattribute of the element you would like to access. 
Here's an example where we need to access the DOM element to focus on the input field when the component mounts:
import {useRef, useEffect} from 'react';
function InputFocus() {
  // `useRef` implementation
  const inputRef = useRef();
  useEffect(() => {
    inputRef.current.focus();
  }, []);
  return (
    <input
      ref={inputRef}
      type="text"
    />
  );
}
After creating a reference to hold the input element in inputRef , it's assigned to the input field's ref attribute. After mounting, React sets inputRef.current to be the input element.
Advance level - React Hooks Cheat Sheet
1. Testing React Hooks
If your testing solution doesn’t rely on React internals, then testing components with Hooks isn't different from how we usually test components.
Let's assume we have the following component, which counts the number of times you clicked the button:
function Example() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}
We can now test this using React DOM or the React Testing Library to reduce the boilerplate code. But let's see how to test it with vanilla code. To make sure the behavior matches with what happens in the browser, we will wrap the code rendering and updating into multiple ReactTestUtils.act() calls as shown:
// counter.test.js
import { act } from 'react-dom/test-utils';
import Counter from './Counter';
let container;
beforeEach(() => {
  container = document.createElement('div');
  document.body.appendChild(container);
});
afterEach(() => {
  document.body.removeChild(container);
  container = null;
});
it('can render and update a counter', () => {
  // Test first render and effect
  act(() => {
    ReactDOM.createRoot(container).render(<Counter />);
  });
  const button = container.querySelector('button');
  const label = container.querySelector('p');
  expect(label.textContent).toBe('You clicked 0 times');
  expect(document.title).toBe('You clicked 0 times');
  // Test second render and effect
  act(() => {
    button.dispatchEvent(new MouseEvent('click', {bubbles: true}));
  });
  expect(label.textContent).toBe('You clicked 1 times');
  expect(document.title).toBe('You clicked 1 times');
});
2. Data fetching with React Hooks
Data fetching is quite a typical pattern we see when working with a React app. But if you are using Hooks in a particular app component, then here's how you should fetch the data.
First, let's say we have this basic App component that shows a list of items:
import React, { useState } from 'react';
function App() {
  const [data, setData] = useState({ hits: [] });
  return (
    <ul>
      {data.hits.map(item => (
        <li key={item.objectID}>
          <a target="_blank" href={item.url}>{item.title}</a>
        </li>
      ))}
    </ul>
  );
}
export default App;
Both the state and the state update function come from the useState Hook. Now, let's use the Axios library to fetch data here. We can implement our effect Hook as follows:
import React, { useState, useEffect } from 'react';
import axios from 'axios';
function App() {
  const [data, setData] = useState({ hits: [] });
  useEffect(async () => {
    // Using Axios to fetch data from an API endpoint
    const result = await axios(
      'https://hn.algolia.com/api/v1/search?query=redux',
    );
    setData(result.data);
  });
  return (
    <ul>
      {data.hits.map(item => (
        <li key={item.objectID}>
          <a target="_blank" href={item.url}>{item.title}</a>
        </li>
      ))}
    </ul>
  );
}
export default App;
But if you run the app, you will see the code is stuck in a loop. The effect Hook runs when the component mounts but also when it updates. We are setting the state after every data fetch. To fix this, we only want to fetch the data when the component mounts.
Hence, we should provide an empty array as the second argument to the useEffect Hook as shown:
...
useEffect(async () => {
    const result = await axios(
      'https://hn.algolia.com/api/v1/search?query=redux',
    );
    setData(result.data);
  }, []);
...
To further improve the code quality, we can use an async function inside useEffect so that it returns a cleanup function in the Promise. So our final code will be:
import React, { useState, useEffect } from 'react';
import axios from 'axios';
function App() {
  const [data, setData] = useState({ hits: [] });
  useEffect(() => {
    const fetchData = async () => {
      const result = await axios(
        'https://hn.algolia.com/api/v1/search?query=redux',
      );
      setData(result.data);
    };
    fetchData();
  }, []);
  return (
    <ul>
      {data.hits.map(item => (
        <li key={item.objectID}>
          <a target="_blank" href={item.url}>{item.title}</a>
        </li>
      ))}
    </ul>
  );
}
export default App;
3. Creating a loading indicator with Hooks
Using the same data loading example as above, we can start implementing a basic loading indicator. For this, we will add another state value using the useState Hook:
const [isLoading, setIsLoading] = useState(false);
Now inside of our useEffect call, we can set the toggle for the true and false values for setIsLoading:
useEffect(() => {
  const fetchData = async () => {
    setIsLoading(true);
    const result = await axios(url);
    setData(result.data);
    setIsLoading(false);
  };
    fetchData();
}, [url]);
Once the effect is called for data fetching when the component mounts or the URL state changes, the loading state is set to true. Here's the entire code after these changes:
function App() {
  const [data, setData] = useState({ hits: [] });
  const [query, setQuery] = useState('redux');
  const [url, setUrl] = useState(
    'https://hn.algolia.com/api/v1/search?query=redux',
  );
  const [isLoading, setIsLoading] = useState(false);
  useEffect(() => {
    const fetchData = async () => {
      setIsLoading(true);
      const result = await axios(url);
      setData(result.data);
      setIsLoading(false);
    };
    fetchData();
  }, [url]);
  return (
    <Fragment>
      <input
        type="text"
        value={query}
        onChange={event => setQuery(event.target.value)}
      />
      <button
        type="button"
        onClick={() =>
          setUrl(`http://hn.algolia.com/api/v1/search?query=${query}`)
        }
      >
        Search
      </button>
      {isLoading ? (
        <div>Loading ...</div>
      ) : (
        <ul>
          {data.hits.map(item => (
            <li key={item.objectID}>
              <a target="_blank" href={item.url}>{item.title}</a>
            </li>
          ))}
        </ul>
      )}
    </Fragment>
  );
}
4. Data aborting using Hooks
We often see that the component state is set even though the component was already mounted. Due to this, we also want to abort data fetching in our component so that we don't encounter any bugs or loops.
We can use the effect Hook, which comes with a cleanup function and runs when a component unmounts. Let's use it to prevent data fetching in the following example:
const useDataApi = (initialUrl, initialData) => {
  const [url, setUrl] = useState(initialUrl);
  const [state, dispatch] = useReducer(dataFetchReducer, {
    isLoading: false,
    isError: false,
    data: initialData,
  });
  useEffect(() => {
    let didCancel = false;
    const fetchData = async () => {
      dispatch({ type: 'FETCH_INIT' });
      try {
        const result = await axios(url);
        if (!didCancel) {
          dispatch({ type: 'FETCH_SUCCESS', payload: result.data });
        }
      } catch (error) {
        if (!didCancel) {
          dispatch({ type: 'FETCH_FAILURE' });
        }
      }
    };
    fetchData();
    return () => {
      didCancel = true;
    };
  }, [url]);
  return [state, setUrl];
};
Here, we used a boolean called didCancel so that the data fetching code knows about the component's state (mounted or unmounted). If it unmounts, the flag is set to true, which prevents setting the component state after the data fetching has been resolved asynchronously.
5. Getting previous props or states with Hooks
Sometimes we need previous props of a component to clean up a used effect. To illustrate this, let's say we have an effect that subscribes to a socket based on the userId props. If this prop changes, we want to unsubscribe from the previously set userId and then subscribe to the next one.
For this, we can use a cleanup function with the useEffect function as:
useEffect(() => {
  ChatAPI.subscribeToSocket(props.userId);
  return () => ChatAPI.unsubscribeFromSocket(props.userId);
}, [props.userId]);
Here, first the ChatAPI.unsubscribeFromSocket(3) will run and then ChatAPI.unsubscribeFromSocket(4) in order when userId changes. Hence, the cleanup function does the work for us. It captures the previous userId in a closure.
Conclusion - React Hooks Cheat Sheet
In this React Hooks cheat sheet, we covered all the major topics related to Hooks. From why it is needed, to how to unlock Hook's superpowers to manage and handle your app state effectively, everything is covered with relevant examples.
We hope this cheat sheet will help you in your future or previous React applications.

