Too many arrows
May 17th 2025Arrow functions should only be used for anonymous callbacks or event handlers.
Consider this snippet:
const isThisWhatIWasLookingFor =
You continue reading:
const isThisWhatIWasLookingFor = ({
foo,
bar,
baz,
}: {
foo: Foo;
bar: Bar;
baz: Baz;
})
Still unclear.
const isThisWhatIWasLookingFor = ({
foo,
bar,
baz,
}: {
foo: Foo;
bar: Bar;
baz: Baz;
}) => {
};
Only after seeing => do you realize it's a function, not a variable. You've wasted time and energy.
Using the function keyword solves this. And you even save some characters:
function isThisWhatIWasLookingFor({
foo,
bar,
baz,
}: {
foo: Foo;
bar: Bar;
baz: Baz;
}) {
}
Even shorter arrow functions make you doubt for a fraction of a second. You don't know if it's a regular variable or a function.
const process = (data: Data) => {
};
Another problem: arrow functions can't have inline default exports. So you usually have to scroll down to find what's being exported:
const doStuff = () => {
}
// ...
export default doStuff
This is especially bad in React projects, where files sometimes have multiple components and the main one is the default export:
const MyComponent = (m: MyComponentProps) => {
return (
<div>
{/* ... */}
</div>
);
}
// ...
const Header = () => (
<header>
{/* ... */}
</header>
);
// ...
const Something= () => {
return (
<div>
{/* ... */}
</div>
);
}
// ...
export default Header;
An export default function at the top is clearer:
export default function Header() {
return (
<header>
{/* ... */}
</header>
)
}
// rest...
Again, arrow functions are best for callbacks and event handlers:
useEffect(() => {
stuff()
return () => cleanup();
}, []);
things.map((t) => ({ a: process(t), b: stuff(t)}))
<button onClick={() => setClicked(true)}>Button</button>
Even if small, these details add up. Specially in big, old and crappy codebases. We should try to reduce the cognitive load as much as possible.