5 ms·
It’s kind of funny how cryptic react can be. Very easy to miss that having an empty [] as a second parameter will result in completely different functionality.
by bottlepalm 4y ago
It’s kind of funny how cryptic react can be. Very easy to miss that having an empty [] as a second parameter will result in completely different functionality.
React is pretty nice overall, just why do we design things to be so unintuitive. Dark patterns.
- ajkjk 4y agoI think that particular choice was an unusually big mistake. Most of them are not so big. But I can't fathom why they made "empty array" cause no rerenders but "no array" cause all the rerenders.
- culi 4y agoThere's a few, rare, use-cases where you'd actually want this behavior. But I agree it's too easy to accidentally do this thing that should be an extreme edge case.
- sli 4y agoI've run into those cases a couple times before and my immediate thought was that it seems unwise for this one function to be used in both cases. There really ought to be something specifically intended for effects with no dependencies even if all it does is make the difference explicit. I'm not a fan of things that contain features that manifest like subtle traps because determining programmer intent is undecidable. That lesson was learned from PHP <=5.
- imbnwa 4y agoAt the very least make them separate functions
- ajkjk 4y agoYeah, that's what I'm saying. There's no reason they had to be the same thing.
- eyelidlessness 4y agoThis is why whenever I can (i.e. it’s not a breaking API change or it’s agreed on), I turn these implicit rules into explicit options named and documented as such in the code. Yeah it’s a little more to type, but it solves the “this isn’t obvious for good reasons” problem much better than a pair of defaults that you probably don’t even know how to look up.
- systoll 4y agouseEffect is treating undefined as meaning undefined. It makes perfect sense from inside of useEffect. If there are no dependencies ([]), the dependencies can never change, so React can always reuse the old effect. If the dependencies aren’t defined, there’s no way to tell if the old effect is OK, so React must always rebuild it. ** But `useEffect(()=>{})` doesn’t explicitly show that React will get `dependencies = undefined`, and in this case that’s unusually important. You could have ESlint force you to change that to `useEffect(()=>{}, undefined)`, I suppose…
- bottlepalm 4y agoYes we know how it works. What we’re saying is that it is a cryptic design. Just like the function returned from useEffect is used for unmounting the component. Not intuitive at all.
- darepublic 4y agothere is an eslint rule exhaustive-deps which really helps with this
- systoll 4y agoFor useMemo and useCallback, Typescript and exhaustive-hooks will both tell you to add a dependency array, because without it those hooks do nothing. For useEffect, exhaustive-hooks & Typescript say nothing, because a non-memoised Effect is a reasonable thing to write. It’s just not a great thing to write accidentally.
- purple_ferret 4y agoI'm pretty sure because it's basically an easy hack to recreate the behavior of componentDidMount.
- culi 4y agoI've found that people that learned class-based React first and then switched to modern function-based React often have more trouble with useEffect than people who learned functional components directly I think a big part of the problem is we need to stop selling useEffect as a replacement for the lifecycle methods of class based components. It also probably would've been a lot less confusing if we called it something like useSideEffect I think any react linting setup resolves most of the confusion but there's a lot of people that start off and don't even know how to set up lint rules for react. They should be a default in any react app imo
- luisgvv 4y agoI can second this... I inherited a huge pile of crap without a react lint ruleset and as soon as I put it in place there were ton of errors. The part I don't like about useEffect is that developers tend to overuse it and when they get stuck in infinite render loops you can see the whole mess it can lead to and how hard it can be to untangle monkey patched logic.
- orangepanda 4y ago> I've found that people that learned class-based React first and then switched to modern function-based React often have more trouble with useEffect than people who learned functional components directly I’d say its the opposite; people who used lifecycle methods know that useEffect and friends eliminates an entire class of bugs. People who started with hooks dont understand the problems it solved, and only see the quirks
- chrisbaker98 4y agoI'm far more experienced with React function components than with class components, so I'm curious, what are some of the problems with class components and how did hooks solve them?
- spion 4y agoThis was not the only design possible. The main problem with lifecycle methods was that you couldn't have multiple of them. There is absolutely nothing about the design of React that necessitated hooks to fix that issue. You could have this API this.addListener('mount', () => { // do things on mount // return cleanup to be called on unmount return () => cleanup() }) called from the constructur of a component. With this you could setup multiple listeners and make sure they're all cleaned up. But no, the syntax wasn't "clean" (you'd pass `this` as an argument to the "custom hooks" equivalent"), so instead we got this error-prone order-dependent design that doesn't allow conditional execution and runs on every render.
- technion 4y agoThe irony is that the whole sell for hooks were that they were easier than classes, which people argued were too hard to reason with. There has been so much hype for how much easier things are with hooks and yet here we are.
- kabes 4y agoI agree it's not easier, but I thought the main argument was better composability. And I think that it achieves that goal quite well.
- nnurmanov 4y agoWith hooks we have now to dialects of the same framework. It is so confusing some time!
- rhaway84773 4y agoHooks allow functional components to be used for non pure components. Which is a huge win IMO because the whole class/object paradigm in JavaScript is broken, and tracking what ‘this’ might be is literally impossible. I think that’s sufficient reason to use hooks. Hooks bring a completely new paradigm and I think it was brought out of Beta too early, so React now has to stick with certain concepts that appear bad ideas in hindsight (a fairly simple and relevant example would be that running useEffect on every render should not have been the default…I suspect the React team could have swapped the behaviors for empty array dependency and no dependency parameter, and it wouldn’t be any more logically weird than what we have today and the default behavior would have been far less footgun-ny). I do think the general idea behind hooks is excellent. I think some of the existing choices, however, need a significant rethink, with the additional real world experience the React devs have with it now.
- spion 4y ago> Which is a huge win IMO because the whole class/object paradigm in JavaScript is broken, and tracking what ‘this’ might be is literally impossible. I chuckled. `this` is a piece of cake to understand compared to hooks. `this` is a function argument that is typically passed to the function by placing it left of the dot at the time when you call the function. That's all you need to know - now you understand `this`. Hooks are a whole nightmare in comparison - there is a stateful counter that assigns an index to every `useState` call just to determine which result you need to get back. This is a terribly error-prone design that passed review with lots of dubious justifications.
- Waterluvian 4y agoYou can make it a linting error. There’s a lot of functions that can behave wildly different in very subtle ways. I always enjoy quizzing people on what breaks about this and why it’s not obvious: myStringNumbers.map(parseInt) Because of this I find it very smart to tool up with opinionated linting rules + TypeScript. Won’t catch everything but it covers a lot of easy mistakes that exist everywhere.
- foota 4y agoYikes, I had to look up why that doesn't work as intended. For others, map passes the value, index, and full array to a function, which is a noop for a fn of 1 argument, but parseInt takes a second argument, the radix to parse. So each iteration actually calls parseInt(value, index), which gives unexpected values.
- bigDinosaur 4y agoIMO this is a more fundamental issue with 'map'. A separate indexed map would make sense because you expect functions passed to map to take one parameter in almost all cases and usually when you need indices you should just use a loop.
- greggman3 4y agoI agree with you. I ran into that issue once. Of course I knew map takes a function that it passes value, index, array, I just forgot for a moment. It would arguably be better to have different functions for those. Very tangentially related, Apple's Metal API has a function that copies a texture. You pass it a width, height, etc... But, if the texture is compressed, then 255 of 256 possible value combos you pass it will be invalid since compressed textures can only be copied in block multiples. I think it would have been a better designed function if it only took width and height in blocks instead of pixels (with uncompressed textures defined has having 1x1 pixel blocks). Then this nonsense of 255 of 256 values being bad would disappear. There's a ton of other inconsistencies in that function. For example, you pass it destinationBytesPerRow when copying to a buffer but if the texture is compressed you pass it say 40 rows and it will only only actually copy 10 rows of blocks and only advance the destination every 4 rows instead of every row. It's arguably a poorly designed function. Thought, I suspect it was inspired by similarly poorly designed functions in other graphics APIs
- KyeRussell 4y agoDark pattern implies malice. IMO this is at worst poor forethought.
- purple_ferret 4y agoIt's a design choice to import the functionality of componentDidMount into the hooks pattern
- spion 4y agoThe original design was fine, the hooks redesign was a total miss