Exploring Conditional Execution of React Hooks

Discover how to conditionally execute React Hooks for more flexible functionality in your React applications#react#hooks#components
October 3 · 3 min read

React hooks can't be called conditionally (see the rules of hooks). Except for the use() hook, which is experimental unless you're using Next.js 13. However, we can work around this in two different ways:

Component approach

React is all about components, and hooks get executed inside components. We can conditionally render components. Put your hook inside a component and call it a day.

function Parent() {
  const [shouldExecuteHook, setShouldExecuteHook] = useState(false)

  return (
    <>
      {shouldExecuteHook ? <ChildHook /> : null}

      <button onClick={() => setShouldExecuteHook(current => !current)}>
        Toggle
      </button>
    </>
  );
}

function ChildHook() {
  useEffect(() => {
    console.log('The hook is mounted!');

    return () => {
      console.log('The hook is unmounted!');
    }
  }, [])

  return null;
}

This is useful when you don't have control over the hook's implementation or don't want to update it to support a disabled/enabled flag.

Disabled/Enabled flag

If we have control over the implementation of the hook, you could add a disabled or enabled flag to its input API, which we can use to control what happens when it executes.

Note that this example doesn't prevent the hook and the hooks used in its implementation from running. As you can see in the following example, what we do to prevent the hook from doing what it's supposed to do is check the flag and return early if the hook is disabled. That's because the useEffect hook will still run.

function useMyHook(disabled: boolean) {
  useEffect(() => {
    if (disabled) {
      return;
    }

    console.log('The hook is mounted!');

    return () => {
      console.log('The hook is unmounted!');
    }
  }, [enabled])
}

function Parent() {
  const [shouldExecuteHook, setShouldExecuteHook] = useState(false)

  useMyHook(shouldExecuteHook)

  return (
    <button onClick={() => setShouldExecuteHook(current => !current)}>
      Toggle
    </button>
  );
}

Which one to pick?

Honestly, the experience will tell you because each hook is a different world. Maybe the following section with questions and answers helps you pick what you need:

Questions and answers

Is this hook returning a value needed in the component where it's used?

Flag approach. Here's an example from the famous react-query library.

I have a hook from an external library, which I want to call conditionally. The hook is used to trigger a side effect.

Component approach. Here's a quick example use case for analytics.

// External hook, will hook into React Router's location state and send events to Google Analytics
useRouterAnalytics()

// In our app, if our users don't consent to tracking, we can wrap this in a component
function RouterAnalytics() {
  useRouterAnalytics();

  return null;
}

// Then, we can conditionally use it on our app:
function AppComponent() {
  const enableTracking = ... get this from somewhere idk ...

  return (
    <>
      {enableTracking ? <RouterAnalytics /> : null}
      
      <OtherComponents />
    </>
  )
}

I followed the Component approach, but now I need to reset the state of the hook; what do I do?

key to the rescue, we're working with a React Component; we can use the key prop to reset its state.

function ConsoleLogCount() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      setCount(current => current + 1)
    }, 1000);

    return () => {
      clearInterval(interval);
    }
  }, []);

  useEffect(() => {
    console.log(count);
  }, [count]);

  return null;
}

function AppComponent() {
  const enableCount = ... get this from somewhere idk ...
  const [resetCountKey, setResetCountKey] = useState(0)

  return (
    <>
      {enableCount ? <ConsoleLogCount key={resetCountKey} /> : null}

      <button onClick={() => setResetCountKey(key => key + 1)}>
        Reset count
      </button> 
      
      <OtherComponents />
    </>
  )
}

Hope you liked the article! If you have more questions, feel free to reach out on social media or email :)


Share article

Where to find me

Made with by me Source code available on GitHubFollow me on Twitter if you want to know more about my future articles, projects, or whatever I come up with!