Lumin
Posted on November 15, 2022
Related Plugin
-
eslint-plugin-react
-
react/hook-use-state
-
-
eslint-plugin-react-hooks
react-hooks/rules-of-hooks
react-hooks/exhaustive-deps
ESLint React Hooks Plugin's Rules
Rules of Hooks (error)
useHookInsideLoop
React Hook "XXX" may be executed more than once. Possibly because it is called in a loop. React Hooks must be called in the exact same order in every component render.
desc: disallowed hook inside loop
```jsx
function ComponentWithHookInsideLoop() {
while (cond) {
useHookInsideLoop();
}
}
```
useConditionalHook
React Hook "XXX" is called conditionally. React Hook must be called in the exact same order in every component render. ~Did you accidentally call a React Hook after an early return?
desc: disallowed conditionally hook
```jsx
function ComponentWithConditionalHook() {
if (cond) {
useConditionalHook();
}
}
function useHook() {
if (a) return;
useState();
}
function useHook() {
try {
f();
useState();
} catch {}
}
```
classHook
React Hook "XXX" cannot be called in a class component. React Hooks must be called in a React function component or a custom React Hook function.
desc: disallowed hook in class
```jsx
class C {
m() {
This.useHook();
Super.useHook();
}
}
```
functionError
React Hook "XXX" is called in function "SSS" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use".
desc: only use hook in Component or hook function
```jsx
function normalFunctionWithHook() {
useHookInsideNormalFunction();
}
```
topLevelError
React Hook "XXX" cannot be called at the top level. React Hooks must be called in a React function component or a custom React Hook function.
desc: disallowed use hook at the top level
```jsx
Hook.useState();
Hook._useState();
Hook.use42();
Hook.useHook();
Hook.use_hook();
```
useHookInsideCallbackError
React Hook "XXX" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function.
desc: disallowed use hook in callback or hook
```jsx
function ComponentWithHookInsideCallback() {
useEffect(() => {
useHookInsideCallback();
});
}
```
useEventError
function created with React Hook "useEvent", and can only be called from the same component. They cannot be assigned to variables or passed down
```jsx
// bad
function MyComponent({ theme }) {
const onClick = useEvent(() => {
showNotification(theme);
});
return <Child onClick={onClick}></Child>;
}
// good
const MyComponent = ({ theme }) => {
const onClick = useEvent(() => {
showNotification(theme);
});
return <Child onClick={() => onClick()}></Child>;
};
```
Exhaustive Deps (warning)
Missing dependency
warn: React Hook useCallback has a missing dependency: 'props.foo'. Either include it or remove the dependency array.
function MyComponent(props) {
useCallback(() => {
console.log(props.foo?.toString());
}, []);
}
Does nothing
warn: React Hook useMemo (and useCallback) does nothing when called with only one argument. Did you forget to pass an array of dependencies?
function MyComponent(props) {
const value = useMemo(() => { return 2*2; }); // failed
const fn = useCallback(() => { alert('foo'); }); // failed
}
No callback
warn: React Hook useEffect requires an effect callback. Did you forget to pass a callback to the hook?
function MyComponent() {
useEffect()
useLayoutEffect()
useCallback()
useMemo()
}
Unnecessary dependency
warn: React Hook useMemo has an unnecessary dependency: 'local2'. Either exclude it or remove the dependency array.
function MyComponent() {
const local1 = {};
const local2 = {};
useMemo(() => {
console.log(local1);
}, [local1, local2]);
}
// useRef doesn't cause re-render
function MyComponent({ activeTab }) {
const ref1 = useRef();
const ref2 = useRef();
useEffect(() => {
ref1.current.scrollTop = 0;
ref2.current.scrollTop = 0;
}, [ref1.current, ref2.current, activeTab]);
}
Out of scope dependency
warn: Outer scope values like 'local1' aren't valid dependencies because mutating them doesn't re-render the component.
function MyComponent() {
const local1 = someFunc();
function MyNestedComponent() {
const local2 = {};
useCallback(() => {
console.log(local1);
console.log(local2);
}, [local1]); // failed
}
}
function MyComponent() {
useCallback(() => {}, [window]); // failed
}
Duplicate dependency
warn: React Hook useEffect has a duplicate dependency: 'local'. Either omit it or remove the dependency array.
function MyComponent() {
const local = {};
useEffect(() => {
console.log(local);
console.log(local);
}, [local, local]);
}
Immutable dependency
warn: The 'foo' literal is not a valid dependency because it never changes. You can safely remove it.
function MyComponent() {
useEffect(() => {}, ['foo']);
}
Preferred array literator
warn: React Hook useEffect was passed a dependency list that is not an array literal. This means we can't statically verify whether you've passed the correct dependencies.
// incorrect
function MyComponent() {
const local = {}
const dependencies = [local];
useEffect(() => {}, dependencies);
}
// correct
function MyComponent() {
const local = {}
useEffect(() => {}, [local]);
}
Spread element dependency
warn: React Hook useEffect has a spread element in its dependency array. This means we can't statically verify whether you've passed the correct dependencies.
function MyComponent() {
const local = someFunc();
useEffect(() => {
console.log(local);
}, [local, ...dependencies]);
}
Complex expression dependency
warn: React Hook useEffect has a complex expression in the dependency array. Extract it to a separate variable so it can be statically checked.
function MyComponent(props) {
useEffect(() => {
console.log(props.items[0]);
}, [props.items, props.items[0]]);
}
Assign value inside hook
warn: Assignments to the 'value2' variable from inside React Hook useEffect will be lost after each render. To preserve the value over time, store it in a useRef Hook and keep the mutable value in the '.current' property. Otherwise, you can move this variable directly inside useEffect.
function MyComponent(props) {
let value;
let value2;
let value3;
let value4;
let asyncValue;
useEffect(() => {
if (value4) {
value = {};
}
value2 = 100;
value = 43;
value4 = true;
console.log(value2);
console.log(value3);
setTimeout(() => {
asyncValue = 100;
});
}, []);
}
Ref value on clean up
warn: The ref value 'myRef.current' will likely have changed by the time this effect cleanup function runs. If this ref points to a node rendered by React, copy 'myRef.current' to a variable inside the effect, and use that variable in the cleanup function.
function MyComponent() {
const myRef = useRef();
useEffect(() => {
const handleMove = () => {};
myRef.current.addEventListener('mousemove', handleMove);
return () => myRef.current.removeEventListener('mousemove', handleMove);
}, []);
return <div ref={myRef} />;
}
Always change dependency
warn: The 'handleNext' function makes the dependencies of useEffect Hook (at line 11) change on every render. Move it inside the useEffect callback. Alternatively, wrap the definition of 'handleNext' in its own useCallback() Hook.
function MyComponent(props) {
let [, setState] = useState();
function handleNext(value) {
setState(value);
}
useEffect(() => {
return Store.subscribe(handleNext);
}, [handleNext]);
}
function Component() {
const foo = class {};
useMemo(() => foo, [foo]);
}
No dependency
warn: React Hook useEffect contains a call to 'setData'. Without a list of dependencies, this can lead to an infinite chain of updates. To fix this, pass [] as a second argument to the useEffect Hook.
function Hello() {
const [data, setData] = useState(0);
useEffect(() => {
fetchData.then(setData);
});
}
Async useEffect
warn: Effect callbacks are synchronous to prevent race conditions. Put the async function inside useEffect
function Thing() {
useEffect(async () => {}, []);
}
Prefer inline function
warn: React Hook useEffect received a function whose dependencies are unknown. Pass an inline function instead.
function MyComponent() {
const local = {};
useEffect(debounce(() => {
console.log(local);
}, delay), []);
}
Posted on November 15, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
May 6, 2024