Less Known React Hooks

React introduced hooks in version 16.8 since then we love and embrace them. And use hooks everywhere in our application. But 90% of the time we use "useState" and "useEffect" hooks. But react has a pile lot more of them. They are really powerful but we don't generally need them. That makes those hooks harder to learn and reason about. Maybe some of you have never heard of them. So let's take a look at them and try to understand what are the use cases of these hooks.

I've added links to each hooks official document, please read them first. They're much better :)

useReducer

A reducer is a function that reduces previous state and an action to a new state. If you're familiar with redux than you know what reducer is and how it works.

const reducer = (prevState, action) => newState

useReducer is useState on steroids. It handles state using reducer concept. You can use both useReducer and useState to handle state in your application. So you might ask why do we need two things that does the same job. While they do each others job, they do it in a different way so each has its own use case.

Use useReducer;

  • If you have a nested, complex state logic that depends on each other
  • If you have state updates that depends on previous state
  • If you have to update more than one field of your state in one action

Other than that go with the useState.

useCallback

useCallback creates a memoized function for you to safely use in your effects. I'll try to explain this with example. Let's say we have a component that fetches some data from some url, and we want it to fetch stuff whenever url changes;

function FancyComponent({ url }) {
  asycn function getThings() {
    try {
      const response = await fetch(url);
      // doStuffWithResponse
    } catch (error) {
      // handle error
    }
  }

  useEffect(() => {
    getThings();
  }, [url]);
}

But as soon as you write such component you get some complains from your linter;

React Hook useEffect has a missing dependency: 'getThings'. Either include it or remove the dependency array. eslint(react-hooks/exhaustive-deps)

What this error says, you have a dependency to the getThings function in your effect, and you need to add that function to the dependency list in order to make sure your component state is sync with the state of the world. But if you add that function to your dependency list you find yourself in an infinite loop hell. Since your function will be created in each render it'll be different from the previous one. So it keeps entering the effect, fetching data, rendering new state and doing it all over again since the function is "changed". (React uses the Object.is comparison algorithm) But if you wrap your function with useCallback hook, you make sure that you get new function only when it needs to be changed;

function FancyComponent({ url }) {
  const getThings = React.useCallback(async () => {
    try {
      const response = await fetch(url);
      // doStuffWithResponse
    } catch (error) {
      // handle error
    }
  }, [url])

  useEffect(() => {
    getThings();
  }, [getThings]);
}

Side note; please don't disable eslint rule for exhaustive dependencies when possible to fix it. Let's say you get a filter prop that you should send to the fetch function and you should also listen to the changes on that prop in order make sure your state is up-to date. If you disable eslint warning and use the old version of this code, you can easily forget to add that parameter to your dependency list and go with it, until a bug comes up. But if you fix the eslint warning with useCallback, as soon as you add that filter logic to your memoized callback, linter will warn you about adding that filter prop to the dependency list. So having a smart helper at hand is very effective why disable it?

useMemo

This hook takes a function, and return value of this function gets memoized. Meaning it doesn't get calculated every time component re-renders. Use this hook as a performance optimization when the calculation logic is too expensive. Or when you really need to memoize that value.

useRef

You can use this hook to access a dom node for operations such as focusing an element. But I've never used that hook to do such thing. And you should try to avoid making imperative operations in your application. I've used this hook to find out if a component is mounted or not to avoid memory leaks.

useImperativeHandle

You can use this hook to expose some functionality of a child component to its parent. I can't think of any use case scenarios of this hook. But if you find yourself using this hook than most probably (99%) you're doing something wrong. Try some state lifting or changing your component composition. Avoid making imperative stuff with react. Great read on this topic: Imperative vs Declarative Programming.

useLayoutEffect

This is actually very similar to useEffect. But as opposed to useEffect, this one runs synchronously, meaning it blocks UI updates. Avoid using it unless you really need to make visual changes to the dom before rendering finishes.

useDebugValue

This hooks comes in handy when you use custom hooks and react-dev tools. With this hook you can choose what to show on react-dev tools for your custom hooks. You can even format your hooks name with a second argument passed as a callback function.