8 ms·
It really IS weird to reason about. `useEffect(f, [])` runs on every render, but only installs the effect once. The other times its not installed. That means s
by spion 4y ago
It really IS weird to reason about.
`useEffect(f, [])` runs on every render, but only installs the effect once. The other times its not installed. That means standard language facilities such as *closure capture* don't quite work as expected unless you pass them in that list `[]` too
Its not impossible to get used to it, but thinking in hooks is profoundly different than thinking with normal javascript.
I think the original design wanted so badly for state to fully live outside of React, but unfortunately, the information about what components are currently active on the screen is very relevant for what the state should be doing. As such, I think that approach just doesn't work no matter how much we'd like it to work.
- rimunroe 4y agoI never said they weren't weird to reason about. Side effects are hard to reason about. I was responding to the claim that hooks "push the boundaries of the language". Closures work exactly the same as they always do, but they're also something a lot of people struggle with and always have. This is all entirely normal JavaScript, but that doesn't mean it's going to be easy for people. Class components were incredibly easy to introduce subtle bugs in, especially because of the abilities to do things like define your own non-idempotent constructor or doing things like assigning to and reading from instance properties (refs are like this too, but I've seen much jankier code around class instances than the--already considerable amount--I've seen with refs). Hooks make you run into those potential bugs head-first, and that's always seemed to me like what a large part of the "hooks are hard to reason about" complaints stem from. The other parts are people who expect functions to be invoked in a different way than they are. I don't really know what to tell those people beyond asking them if they've ever seen code calling a component directly.
- spion 4y ago> Closures work exactly the same as they always do, but they're also something a lot of people struggle with and always have. Its not normal for a render function to run all the time but the closures mentioned in it to be completely ignored. Thats completely the opposite of how most other APIs work in the language. Usually, a (constructor) function runs once, and the closures returned by it may get run multiple times. Completely ignoring the passed closures to hooks in many/most cases is something very specific to React.
- rimunroe 4y ago> Its not normal for a render function to run all the time but the closures mentioned in it to be completely ignored. Thats completely the opposite of how most other APIs work in the language. Usually, a (constructor) function runs once, and the closures returned by it may get run multiple times. React function components are not constructor functions though, so I don't see how those are relevant. Bringing up the expectation that constructors "only run once" in the context of how hooks are hard to reason about seems strange though, as class component constructor functions have to be written in a way where they can potentially run multiple times. I think that's been a requirement for the entire time React has supported JavaScript classes. What do you mean by "closures returned by a constructor function"? Do you mean the instance and its associated methods? If so, yes, running a constructor obviously produces one instance[1], which is true in React too. But you're never invoking that constructor yourself--and you never should have been doing that with React in the first place--so why would that matter? Also, I'm confused, can you point at a comparable API to a React function component in the JavaScript language? [1] well, sort of. Constructors can return any arbitrary object, so the following works class Foo { constructor(n) { return new Number(n); } } let foo = new Foo(1); foo instanceof Foo; // false foo instanceof Number; // true
- spion 4y ago> React function components are not constructor functions though, so I don't see how those are relevant I'm giving an example of whats a normal use case of closures. Running a single function once that creates closures that run zero, one, or multiple times is "normal". Running a single function multiple times that creates closures that run one time and get ignored all other calls of the function is not.
- rimunroe 4y ago> Running a single function multiple times that creates closures that run one time and get ignored all other calls of the function is not. This exactly describes a leading-edge debounce function, which I was asked in multiple interviews early in my career to implement on a whiteboard. I'll admit that "problems people feel like giving to junior devs" isn't a great method of measuring whether or not something is simple, but I think it is indicative of how widely that sort of thing is expected to be understood. It also exactly describes any sort of request framework where request responses are cached after the first call and the result is returned without a network hit in subsequent calls.