eslint-plugin-react
eslint-plugin-react copied to clipboard
Feature request: ban createRef in function components
Using createRef
in a function component is almost always a mistake because it will create a new ref on every render. Usually you want useRef
(see this discussion). Since this is such an easy mistake to make, it would be helpful to have a linter rule to flag it.
That seems like a good rule for eslint-plugin-react-hooks. This plugin can’t assume you have hooks available, so such a rule wouldn’t be useful for react versions without them.
Thanks @ljharb. I filed an issue against the React repo, we'll see what they say: https://github.com/facebook/react/issues/21256
IMO this does seem like something that should be warned about, but it also seems odd for eslint-plugin-react-hooks
to warn about it (since those rules are all about hooks and createRef
is not a hook).
Regardless of whether the version of React in question supports hooks, using createRef
in a function body like this is always a mistake, right?
Not necessarily - it could be memoized manually, or used inside another memoizing hook.
Not when it's in the top level body of the function component (not within a conditional) as shown in the linked example.
or used inside another memoizing hook.
Also, could you share the example you have in mind of when it would be appropriate for a custom hook to use createRef
rather than useRef
for memoization? :)
(warning: off the top of my head) Something like:
function Foo({ a }) {
const ref = useMemo(() => createRef(), [a]);
const fn = useCallback(() => doSomethingWith(ref?.current), [ref]);
return <Bar onSomething={fn} />;
}
Obviously you might prefer useRef
here instead of useMemo
- but that doesn't mean there's something wrong with this usage.
Obviously you might prefer
useRef
here instead ofuseMemo
- but that doesn't mean there's something wrong with this usage.
Kind of does 😄 Seems to suggest (to me) some fundamental misunderstanding of how the APIs work.
For example, recreating the ref object itself when a dependency changed seems to defeat the purpose of a ref (as a stable, mutable object) in the first place? But also, using a built-in hook and a built-in API to do the same thing as a built-in hook seems to suggest you aren't aware of the built-in hook (and a lint rule telling you about it seems fine).
@ljharb that usage of createRef
seems fine and @bvaughn's caveat about "in the top level body of the function component" would prevent a false positive on it.
That’s all a reasonable position to take.
Unfortunately, eslint-plugin-react-hooks doesn’t export (or depend on a package for) the hook detection logic, which means we have to copy-paste it, which seems very unfortunate.
@bvaughn even though i really do think any rule dealing with hooks whatsoever belongs in the “react hooks” eslint plugin (that’s the implied position you occupied by publishing a so-named eslint plugin, imo), if you think it would be better in this plugin, then could at least the hook logic be factored out so we can reuse it?
cc @gaearon who may have thoughts here. (He's the primary owner of the hooks lint rule.)
While the proper home for this rule up in the air, my workaround is to blanket ban createRef
using the standard no-restricted-imports
and no-restricted-properties
rules and use eslint-disable
to whitelist existing class components.
{
'no-restricted-imports': [
'error',
{
paths: [
{
name: 'react',
importNames: ['createRef'],
message: 'You probably want React.useRef instead.'
}
],
},
],
'no-restricted-properties': [
'error',
{
object: 'React',
property: 'createRef',
message: 'You probably want React.useRef instead.'
}
]
}
I've resorted to adding yet another plug-in, https://github.com/rel1cx/eslint-react, just so that I can enable its no-create-ref rule.