DEV Community

TusharShahi
TusharShahi

Posted on

React Hooks: Default Export vs Named Export

Both default and named exports have their pros and cons. In Javascript codebases, it mostly comes down to personal choice and consistency. I also never felt strongly about one over the other - until I saw this one error.

Problem

The error was minified error #310 - React detected more hooks in a render than the previous one. This usually happens when React hooks are called conditionally.

Example:

const Component = () => {
....
if(someCondition)
 return <p>Testing</p>;
const value = useHook(); //breaks one of React's rules. ESLint gives an error
}
Enter fullscreen mode Exit fullscreen mode

Usually, ESLint throws an error for such hook invocations, courtesy of the plugin - eslint-plugin-hooks-rules. So this code would be caught before it can be committed to main.

But in this failing scenario, it did not happen. The reason is that hook was imported like this:

import getItemConfig from '../config/hooks/item';
Enter fullscreen mode Exit fullscreen mode

The default export from the file is a hook. But it was named something else and imported. If getItemConfig is used conditionally, ESLint rules cannot flag it. The rules rely on the use prefix. That is why it is important to create custom hooks that start with the prefix use.

Fix

Fixing the issue is straightforward. Ensure that the hook is never called conditionally. In this case, move it above the return statement.

If your hook makes a network call that you wanted to prevent and hence it was after the return statement, then consider breaking the component into two.

The rule of React enforcing the number and order of hooks to be called to be the same is only within a React component boundary.

Preventing future occurences

As next steps, we should ensure that hooks are always imported as hooks - functions with prefix use.

We can try to ensure that in our code reviews, but that might not be fool proof as the hook might be committed separately and the usage might be committed in a different commit.

There is no direct advantage of using a default export, and the one advantage we have - renaming an import to anything the developer wants - ended up becoming a disadvantage for us.

We decided to follow the approach for disabling default exports, only for hook files. Note that this does not completely solve the problem. Because even in named exports, one can do the below:

import { useGetConfig as getItemConfig } from '../config/hooks/item';
Enter fullscreen mode Exit fullscreen mode

But this is more intentional and easier to catch in code reviews.

Top comments (0)