React hooks a functional update causes the eslint no-shadow error - reactjs

I'm using React useEffect to update a state. Eslint warns I'm making no-shadow error. I think this is related to some eslint or eslint plugin settings because CRA doesn't cause the same error. How to fix it?
function Sample(props) {
const { flag } = props;
const [value, setValue] = useState();
useEffect(() => {
if (flag) {
setValue(value => value + 1);
}
}, [flag]);
}
Here, setValue(value => value + 1); the value causes no-shadow due to declared at the useState.

eslint is correctly complaining because you've declared a value variable in your useState hook, but then you're re-declaring another variable called value in your useEffect hook.
The simplest fix is to not shadow the variable name by changing the variable in your setValue call -
useEffect(() => {
if (flag) {
setValue(prev => prev + 1);
}
}, [flag]);

Related

React hook missing dependency of custom hook setter

I'm well aware of what the Hook has missing dependency is, what it means and why it's important to watch all dependencies, but this one is just weird.
export function Compo() {
const [value, setValue] = useState<number>();
useEffect(() => {
setValue(Date.now());
}, []);
return (
<>{value}</>
);
}
works fine, but:
function useValue() {
return useState<number>();
}
export function Compo() {
const [value, setValue] = useValue();
useEffect(() => {
setValue(Date.now());
}, []);
return (
<>{value}</>
);
}
show the well known React Hook useEffect has a missing dependency: 'setValue'. Either include it or remove the dependency array react-hooks/exhaustive-deps.
What you've noticed in your example is a quirk of the rule react-hooks/exhaustive-deps. It gives special privilege to hooks it is aware of, and knows to be "stable" under certain circumstances.
Quoting the implementation:
// Next we'll define a few helpers that helps us
// tell if some values don't have to be declared as deps.
// Some are known to be stable based on Hook calls.
// const [state, setState] = useState() / React.useState()
// ^^^ true for this reference
// const [state, dispatch] = useReducer() / React.useReducer()
// ^^^ true for this reference
// const ref = useRef()
// ^^^ true for this reference
// False for everything else.
source: https://github.com/facebook/react/blob/v17.0.1/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js#L152
Specifically, this part of the rule seems to be what is exempting the useState hook's setter under these circumstances:
if (name === 'useState') {
const references = resolved.references;
for (let i = 0; i < references.length; i++) {
setStateCallSites.set(
references[i].identifier,
id.elements[0],
);
}
}
// Setter is stable.
return true;
The unfortunate result of the hook being helpfuln/clever is that it can lead to confusion where its inference doesn't work, like the scenario you just described.

ERROR: React Hook useEffect has a missing dependency: 'dispatch'. Either include it or remove the dependency array

Trying to dispatch an action but i got this error in my terminal.
ERROR: React Hook useEffect has a missing dependency: 'dispatch'. Either include it or remove the dependency array
yet my code wont render on the screen.
function CartPage(props) {
const dispatch = useDispatch();
const [Total, setTotal] = useState(0);
const [ShowTotal, setShowTotal] = useState(false);
const [ShowSuccess, setShowSuccess] = useState(false);
useEffect(() => {
let cartItems = [];
if (props.user.userData && props.user.userData.cart) {
if (props.user.userData.cart.length > 0) {
props.user.userData.cart.forEach((item) => {
cartItems.push(item.id);
});
dispatch(getCartItems(cartItems, props.user.userData.cart)).then(
(response) => {
if (response.payload.length > 0) {
calculateTotal(response.payload);
}
},
);
}
}
}, [props.user.userData]);
}
This is a linting rule exhaustive-deps. The purpose is to get you to only use props or state variables used in your second argument.
Because your second argument does not include dispatch you are getting an error. If you add dispatch to your second argument that should go away.
useEffect(() => {
// your logic here
}, [props.user.userData, dispatch])
The downside to this is that, your effect will run whenever your userData or dispatch changes. You can ignore this rule with // eslint-disable-next-line react-hooks/exhaustive-deps.

How to solve this missing dependency in useEffect?

The goal is to reset a value, that is mutated inside of the function, if the props have changed to something that is different from the current value.
I am trying out react hooks and am still not confident how to solve this and if it can be solved.
While this seems to work as expected, I get a ESlint warning:
Warning:(62, 8) ESLint: React Hook useEffect has a missing dependency: 'value'. Either include it or
remove the dependency array. (react-hooks/exhaustive-deps)
function (info) {
const initialValue = info.value;
const [value, set_value] = useState(initialValue);
useEffect(() => {
if (initialValue !== value) {
set_value(initialValue);
}
}, [initialValue]);
...};
I can't add the value variable to the dependency array, because it will prevent the mutation.
I checked the react-hooks documentation and this thread here:
https://github.com/facebook/react/issues/15865
But am still not confident how to apply this knowledge to my case.
You don't need value in useEffect scope, you can use functional updates:
function App({ value: initialValue }) {
const [value, set_value] = useState(initialValue);
useEffect(() => {
set_value((prevValue) =>
initialValue !== prevValue ? initialValue : prevValue
);
}, [initialValue]);
return <></>;
}

Custom react hook can not be called in useEffect with empty dependencies

Im new to react hook, Im doing a project with new feature "Hooks" of react.
I've faced a problem and I need an explain for it.
As document, to implement "componentDidMount", just pass empty array in dependencies argument.
useEffect(() => {
// some code here
}, []);
And I can call dispatch function to updateState inside this useEffect.
const [flag, setFlag] = useState(false);
useEffect(() => {
setFlag(true);
}, []);
Above code works perfectly without warning or any errors.
Now I have my custom hook, but I can not call my dispatch inside the effect.
const [customFlag, setCustomFlag] = useCustomHook();
useEffect(() => {
setCustomFlag(true);
}, []);
This is my custom hook.
function useCustomHook() {
const [success, setSuccess] = useState(false):
const component = <div>{ success ? "Success" : "Fail" }</div>;
const dispatch = useCallback(success => {
setSuccess(success);
}, []);
return [component, dispatch];
}
With above code, it requires me to put setCustomFlag inside the dependencies array.
I do not understand why. What is different between them?
Thanks for sharing.
Probably, your custom hook returns different instance of setCustomFlag on each call. It means, that useEffect() will always use first value (returned on first render). Try to memoize it by calling useCallback()/useMemo() hooks:
function useCustomHook() {
...
const setCustomFlag = useCallback(/* setCustomFlag body here */, []);
}
It would be nice to have your custom hook source to say more.
The reason is setFlag from useState is a known dependency, its value won't change between render, hence you don't have to declare it as a dependency
React eslint-plugin-react-hooks can't be sure about your custom hook, that's why you need to put that into the dependency list
This is taken from eslint-plugin-react-hooks
// Next we'll define a few helpers that helps us
// tell if some values don't have to be declared as deps.
// Some are known to be static based on Hook calls.
// const [state, setState] = useState() / React.useState()
// ^^^ true for this reference
// const [state, dispatch] = useReducer() / React.useReducer()
// ^^^ true for this reference
// const ref = useRef()
// ^^^ true for this reference
// False for everything else.
function isStaticKnownHookValue(resolved) {
}

React-Native Making setInterval Declarative with React Hooks

I'm playing with React Hooks for more than a few hours, I'm probably ran into an intriguing problem: using setInterval just doesn’t work as I'd expect with react-native
function Counter() {
const [time, setTime] = useState(0);
const r = useRef(null);
r.current = { time, setTime };
useEffect(() => {
const id = setInterval(() => {
console.log("called");
r.current.setTime(r.current.time + 1);
}, 1000);
return () => {
console.log("cleared");
clearInterval(id);
};
}, [time]);
return <Text>{time}</Text>;
}
The code above should clearInterval every time that time state changes
It works fine on ReactJS but on React-native I'm getting an error says "Callback() it's not a function"
enter image description here
It's working as expected in Reactjs
https://codesandbox.io/s/z69z66kjyx
"dependencies": {
"react": "16.8.3",
"react-native": "^0.59.6",
...}
Update:
I tried to use the ref like this example but still getting same error
const [time, setTime] = useState(0);
useInterval(() => {
setTime(time +1);
});
return (<Text>{time}</Text>);
}
function useInterval(callback) {
const savedCallback = useRef();
// Remember the latest function.
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
// Set up the interval.
useEffect(() => {
let id = setInterval(()=>savedCallback.current(), delay);
return () => clearInterval(id);
});
}
Because you are mutating the DOM via a DOM node refs and the DOM mutation will change the appearance of the DOM node between the time that's rendered and your effects mutates it. then you don't need to use useEffect you will want to use useLayoutEffect
useLayoutEffect this runs synchronously immediately after React has performed all the DOM mutations.
import React, {useState, useLayoutEffect,useRef} from 'react';
import { Text} from 'react-native';
const [time, setTime] = useState(0);
useInterval(() => {
setTime(time +1);
});
return (<Text>{time}</Text>);
}
function useInterval(callback) {
const savedCallback = useRef();
// Remember the latest function.
useLayoutEffect(() => {
savedCallback.current = callback;
}, [callback]);
// Set up the interval.
useLayoutEffect(() => {
let id = setInterval(()=>{
console.log('called');
return savedCallback.current();
}, delay);
return () => {
console.log('cleared');
return clearInterval(id);
}
});
}
if you just using useEffect and getting this error
Uncaught TypeError: callback is not a function
at flushFirstCallback (scheduler.development.js:348)
at flushWork (scheduler.development.js:441)
at MessagePort.channel.port1.onmessage (scheduler.development.js:188)
This is a bug in RN because of wrong scheduler version, Unfortunately RN didn't have an explicit dependency on scheduler version by mistak. Dan Abramov already fixed this bug on scheduler version "0.14.0"
To solve the problem just run the following command
npm install scheduler#0.14.0 --save
Or Try adding "scheduler": "0.14.0" to your package.json in dependencies and re-running your package manager
You should be able to still use the state hook variables in the effect hook as they are in scope.
useRef: mutations here aren't tracked, so they don't trigger re-renders.
CodeSandbox Counter Example
I feel that using refs in the way you're trying to is more verbose than just using the state and setter directly. The useRef ref is intended for mutable values over time, but you already get that with the useState hook. The ref works because you're not really mutating the ref, but instead are simply overwriting it each render cycle with the contents of the useState hook that are updated.
I've updated my sandbox to use useRef as the way you have, your useEffect hook was causing your cleanup function to fire on every render, so removed the dependency. You'll notice now you only see "called" until you refresh.

Resources