Reputation: 5111
const comp_ref = useRef();
const { active_page, path_arr, is_active_alert } = useCustomHook();
const { user_loaded, nav_collapse, mobile } = useSelector(
(state) => state.boot
);
useEffect(() => {
if (nav_collapse || mobile) {
if (is_active_alert && comp_ref.current) {
comp_ref.current.style.transform = "translateY(0px)";
}
}
}, [is_active_alert]);
In the useEffect
, it does not have nav_collpase
or mobile
as its dependencies/triggers.
Both nav_collpase
and mobile
are redux states that hold merely boolean
values.
Based on my testing, it still gets the updated values for both nav_collpase
and mobile
whenever the callback is run (due to a change in is_active_alert
).
Question:
useEffect
able to get the updated values for the state variables when it doesn't depend on them?Upvotes: 0
Views: 76
Reputation: 1954
Lexical scoping works in the same manner in both event handlers and useEffect handlers, therefore the answer to the question should already be known to you, let us refresh it.
Let us make a counter-question first.
While running the below sample code, on each click on the first button, the state randomnum is updated with a new random number. When the second button is clicked, it shows an alert of the latest random number.
Now there may be a question here. How does the handler handleAlertNumber get the latest value, even though it does not depend on the state randomnum ?
Let us write our right answer here. It gets the latest value because the state change triggers a new render, and during this new render, the previous version of the handleAlertNumber is destroyed and a new version has been re-created. This is correct.
However there is a point still remain unanswered, it is about the dependency. The handler has no dependency at all, still it got the latest value.
The improved answer may be, since handleAlertNumber does not have a dependency array, it gets redefined or re-created for all state changes in the component. This is absolutely correct.
App.js
import { useState } from 'react';
export default function App() {
const [randomnum, setRandomNum] = useState(Math.random());
function handleAlertNumber() {
alert(randomnum);
}
return (
<>
<button onClick={() => setRandomNum(Math.random())}>
Set a new random number
</button>
<br />
<br />
<button onClick={handleAlertNumber}>Show the random number</button>
</>
);
}
Now coming to your question
Let us compare, an event handler and a useEffect handler. For instance, handleAlertNumber the one we discussed above is an event handler, and a useEffect handler is the one we are discussing in this question.
The answer may be:
handleAlertNumber runs in response to a specific user interaction - button clicking. Similarly, yes can say similarly, useEffect runs in response to a commit phase. It means it runs automatically soon after rendering and commit phases of a component. Therefore both may be generally considered as event handlers run in response to the respective events.
There is a difference though. Both are redefined on different conditions. While handleAlertNumber re-created on all state changes in the component, a useEffect handler is re-created only on the changes in specific states set as its dependency.
Now the answer to the question may be :
The handleAlertNumber is getting redefined on all state changes, therefore it can access the latest state randomnum, this accessibility does not depend on its dependency, which should be very clear by now since this handler has no dependency at all.
The same happens in useEffect handler as well. Its accessibility of states in the enclosing function does not depend on the dependency array. In the nutshell, this accessibility of states irrespective of its dependency is technically called lexical scoping which is the defining characteristic of closures. It means all events handlers in a React component are closure, therefore it can access the state in its enclosing function.
Upvotes: -2
Reputation: 388
Actually, the updated value is always accessed because when the callback fires due to a dependency change, it contains a new reference in memory. I also tried with memorizing the useEffect
callback using useCallback
, and it worked. While the useEffect
callback still fires every time the dependency changes, it doesn't access the updated values. However, it uses the values from the first render (the values it was "memorized" with).
Here's an example of what I tried:
const [count, setCount] = useState(0);
const callback = useCallback(() => {
console.log(count);
}, []);
useEffect(callback, [count]);
I’m not sure if this has a real-world use case, but it’s always good to have the conceptual idea behind it.
Upvotes: 0
Reputation: 203323
How is the callback in the useEffect able to get the updated values for the state variables when it doesn't depend on them?
useEffect
hook is called each and every render cycleIt's only when the useEffect
hook's dependencies change that the callback is actually called, thus seeing whatever the current values are that have been closed over in callback scope.
Upvotes: 3
Reputation: 169338
How is the callback in the useEffect able to get the updated values for the state variables when it doesn't depend on them?
Because if the effect does run, it'll see those values, whatever they are. It'll just not get automatically run if those values change if the values aren't in dependencies.
(For instance, in Strict Mode, effects may be run more than once.)
Use eslint-plugin-react-hooks to warn you about these.
In any case, this looks like bad use of effects to begin with.
Assuming comp_ref
just points to a div
, you could just do
const AlertComponent = () => {
const { active_page, path_arr, is_active_alert } = useCustomHook();
const { user_loaded, nav_collapse, mobile } = useSelector((state) => state.boot);
const shouldTransform = (nav_collapse || mobile) && is_active_alert;
return <div style={shouldTransform ? { transform: "translateY(0px)" } : null}>...</div>;
};
Upvotes: 2