35 ms·
No you're right, I completely agree with you. It's also very telling that there's also this very long article about useEffect dependencies: https://react.dev/le
by davedx 4y ago
No you're right, I completely agree with you. It's also very telling that there's also this very long article about useEffect dependencies: https://react.dev/learn/removing-effect-dependencies https://react.dev/learn/removing-effect-dependencies
useEffect and dependencies are the worst part of React at the moment.
The reason is, you do need to use effects, everywhere in your code, constantly. And dependencies, and the linter rule that goes with them, are nasty ergonomics, and hard to reason about.
The article I linked does explain it all quite well, and most devs I work with do understand the dependencies rules, but the rule still needs to be disabled often. Why?
Often the lint errors really are just wrong. Just like ChatGPT is often simply just wrong, no ifs or buts. For example: you have a callback function declared as an expression, that the useEffect hook calls. The linter will say "oh this is an expression, therefore it needs to be declared as a dependency". But it doesn't, because looking at the code as a human, you can immediately see the function never changes.
If you follow the linter, you will inevitably end up going down a destructive and unnecessary refactoring. You'll end up with unnecessary `useCallbacks` sprinkled everywhere that hurt readability for nothing. Often, you'll have to refactor the dependencies out of the component entirely, splitting one cohesive component into two, just to satisfy the linter out of an abundance of caution.
Junior devs will be confused. Senior devs will be frustrated. Every react codebase I've worked on has gagged this linter warning.
So how should this work?
It shouldn't be a a linter error in the first place. Linters should be used to enforce coding standards, not fix application bugs. I feel very strongly on this.
- zarzavat 4y agoThe funny thing is that React already kind of has its own programming language: JSX. If they wanted to, they could add language features to support React. I expect that would be very controversial though.
- yencabulator 4y agoSvelte is pretty pleasant, compared to React. It's fundamentally a JS-to-JS compiler that adds reactive assignments, and ridiculously much more usable than useEffectHopeThisWasTheRightThing. let count = 1; // this is reactive $: doubled = count * 2; function handleClick() { count += 1; }
- Rapzid 4y agoThere is a new experimental hook, useEffectEvent, whose only discernable functionality based on the documentation and information available is to quiet the linter. I'm not sure that's all it does since I haven't dug into the React source code, but that's what many are speculating.
- andrewingram 4y ago[Edit: this is all wrong, see comment tree] It makes it so that the same instance of a function is used for all renders, but that it always points to the data closed over by the latest render; it eliminates a whole class of scenarios that normally lead to gnarly dependency array tomfoolery with useCallback, useMemo and useEffect. It's quite a common hook to try to invent in user-land (i've seen it in numerous projects), it's not just a linter silencer.
- Rapzid 4y ago> but that it always points to the data closed over by the latest render It's my understanding that this was to be the behavior of the canceled useEvent hooks but that the new useEffectEvent hook does not return a stable reference. Do you have sources for the behavior of the new hook?
- andrewingram 4y agohttps://react.dev/learn/separating-events-from-effects#declaring-an-effect-event https://react.dev/learn/separating-events-from-effects#decla... > Here, onConnected is called an Effect Event. It’s a part of your Effect logic, but it behaves a lot more like an event handler. The logic inside it is not reactive, and it always “sees” the latest values of your props and state.
- Rapzid 4y agoA bare closure would always see the latest values because it closes over them during render. And the latest useEffect closure will have closed over that method.. Here is the test suite for useEffectEvent and the test ensuring it does not provide a stable reference: https://github.com/facebook/react/blob/main/packages/react-reconciler/src/__tests__/useEffectEvent-test.js#L609 https://github.com/facebook/react/blob/main/packages/react-r...
- cbovis 4y ago"For example: you have a callback function declared as an expression, that the useEffect hook calls. The linter will say "oh this is an expression, therefore it needs to be declared as a dependency". But it doesn't, because looking at the code as a human, you can immediately see the function never changes." Can you elaborate on this? In my experience this isn't the case. If a function is declared outside of the component scope then it won't trigger a lint error. If it's defined within the component scope and not wrapped in useCallback then it does in fact change on every render and the lint error is being raised for good reason.
- davedx 4y ago> If it's defined within the component scope and not wrapped in useCallback then it does in fact change on every render and the lint error is being raised for good reason. So I just did some playing around to investigate this further, and it seems to be more subtle than I thought. If you have a function expression but the expression is not dependent on any outer scoped variables, the linter does not complain that you didn't specify the function itself as a dependency in useEffect. Example: ``` let bar = "bar"; const setResult = (result: string) => { // Won't trigger "exhaustive-deps" setBio(result); // Will trigger "exhaustive-deps" if (bar === "1") setBio(result); }; useEffect(() => { setResult(""); }, [person]); ``` So there's some quite sophisticated analysis of the AST going on in the lint rule. I still think that this rule is too complicated for a linter. (Bugs like inter-dependencies in your data and code should be caught by unit tests). But it's technically pretty impressive. Edit: I did some more investigations. If I make a function that depends on something very unpure (like checking window.location), the linter fires if the unpure function is in the outer scope of the hook, but not if it isn't. Even though the dependency is the same. So the linter rule can only identify locally scoped dependencies I think.
- joshribakoff 4y ago> Bugs like inter-dependencies in your data and code should be caught by unit tests Why write a ton of unit tests for what a static check can just do “for free?”. Not sure i follow your logic. I agree React can feel like a nuisance, but the lint rule itself is not the issue i think you have, it sounds like your issue is just with hooks.