7 ms·
Making SetInterval Declarative with React Hooks
- deadA1ias 8y agoDan Abramov claims that React hooks haven't jumped the shark. All I'm seeing in this article is exactly that.
- nwienert 8y agoI’ve now converted a good chunk of a large stack over to them and the best way to describe them is this: they are as big an improvement to React as React was to its predecessors. This example is meant to be a pathological case. The fact that you can build complex interface logic and bundle it into a function that can then be extended and composed is a huge leap forward. I do think Hooks will take time for some of the best ones to become standard. While I see some early “lodash for hooks” like libararies appearing, I could also see a really nice PWA that let you search gists for popular hooks or similar. Hooks are a very nice solution to a very hard problem. But unless you’ve done large scale frontend work and especially more complex interactive components it may be hard to see. Although just getting rid of all the decorators/HOCs is already a big win.
- dbetteridge 8y agoAgreed, we're using hooks over redux on a largish codebase and once you wrap your head around the model the only real frustration is not being able to use them in class components.
- jypepin 8y agoBy "over redux" do you mean "with redux" or "instead of redux"? I haven't used hooks yet but if you are using hooks instead of redux, it confuses me on how hooks work. To me they seems to be replacing the need for a class with local state, not replacing a redux architecture? I'm interested to hear more please :)
- dbetteridge 8y agoSorry should have been clearer. It's more of a combination of react hooks and react context, hooks to deal with local state and context to provide reducer functionality for global state. Just means you can have functional components that respond purely to effects from state changes locally or at global level
- bestest 8y agoFrom my experience I found this kind of separation to be somewhat redundant and am simply using redux to manage all state — both global and local context. This also means that components are simply components, and setTimeouts and everything else is moved out to redux thunk actions. Which makes hooks totally unneeded in my case.
- acemarke 8y agoI'll point out that we absolutely plan to ship hooks as part of the React-Redux library, but we need to do some internal reworking first to make that feasible. See this roadmap issue I just posted for details: https://github.com/reduxjs/react-redux/issues/1177 https://github.com/reduxjs/react-redux/issues/1177
- lj3 8y ago> am simply using redux to manage all state This only works in small apps. I work on a medium sized app at work and we started that way too. It becomes a huge, sloppy mess in short order. Putting everything in Redux is akin to a desktop application that uses nothing but global state. Hooks are great because it keeps the state local, but it can be re-used across components if we need to.
- jypepin 8y agoI would disagree with you and say that from my own experience (not saying your experience is wrong obviously) it's quite the opposite; in small applications, using local states is fine, but as soon as your application grows, having everything inside a central store makes everything cleaner and much simpler, as multiple independent components might want to share state or listen to shared events, etc.
- kaoD 8y ago> The fact that you can build complex interface logic and bundle it into a function that can then be extended and composed is a huge leap forward. Unless you already used HOCs (in the Recompose sense). Every single blog post is obsessed with comparing hooks with classes (I guess because it's the only thing they improve upon). What about HOCs? I'm not sure hooks are really that much of a leap forward compared to them. HOCs only wart is their pollution of props which admittedly hooks solve (by virtue of using local variables), but hooks have their own warts too as evidenced in this article.
- deleted 8y ago[deleted]
- Retsam 8y agoAs someone who uses Typescript, HOCs are a nightmare, and hooks are a world of difference.
- mlsarecmg 8y agoHocs had several problems, they couldn't access context, re-using one result in another is hard, they create a new component for every thing you want to do, etc. Take this example for instance: https://codesandbox.io/embed/26mjowzpr https://codesandbox.io/embed/26mjowzpr It measures out the view, reads out media queries, uses the results to construct a grid, uses a hook to turn it into an animatable, and just spits out the view as if nothing happened. All of this in the same component, with small, re-usable utility functions tapping into the host-components lifecycles. Just by looking at it you know what it's doing. This is how a component should behave from now on, an entity that hooks into the host, instead of a class the host calls into. In my experience this is the first time you can express logic linearly: x > y > z > view, without wraps, inversion of control and DI. Now imagine doing this with hocs ... even stacking two is already destroying any hope of guessing what's going on.
- Jasper_ 8y agoMy eyes glazed over at this. I can follow the first code example with the explicit mount and unmount, but really not any of the others. I will pray I don't have to debug any website using these hooks features any time soon. This "declarative hook" ivory tower is for people with far much more time on their hands than I, I feel.
- karolist 8y agoI've seen the sentiment expressed before and I think it will only get worse in the future. React now is very popular with smaller/medium companies, large enterprises like banks will surely jump to it after some delay. It's usually when the main talent moves off to something more exciting (what happened with rails -> node for example). Even now it's hard to find someone good at idiomatic React, imagine the same situation when it's perhaps not as popular as it once was and internal projects are mature. Codebases using declarative style are very hard for majority of programmers to grok in my experience, mostly because it's such a big paradigm shift and requires truly abstract thinking, also not generally something you learn at CS programs. It will either all be a big mess or require some senior high salaried people do the maintenance. That will be a good time to do frontend contracting for the enterprises and it's coming soon.
- erikpukinskis 8y agoI find declarative style hard because you can’t use a debugger, which means you can’t “go in and look around”. You need to have a theory of what’s happening a priori that you can test. That means you need to model the internals of the tools in your mind to be able to make guesses. That means a slow learning curve. That’s fine if it’s one of one or two major libraries that you use all the time every day. But in practice most of us are using dozens and dozens of tools, tools which change all the time, and we have to learn as we go. So these “big learning up front” APIs are just not very ergonomic for teams to engage. Unless you’re Rails or React or something where you can claim to be basically a programming language or “standard library” in your own right, something everyone on a team will have already dedicated themselves to studying in depth.
- saagarjha 8y ago> Our “impedance mismatch” is not between Databases and Objects. It is between the React programming model and the imperative setInterval API. I haven't used it much, but React gives me the feeling that it's trying to come up with hacks to shoehorn imperative UI (which seemingly can't go away) into the "the state is the UI" model. Anything with timers, or animations, or other things that reach across multiple renders seems to be poke through the nice React model because it by definition requires dropping the abstraction of the UI being defined by the state because the change is more "incremental" than transitioning to a new value. (As an aside, I love the dark mode support on your website!)
- sudhirj 8y ago> React gives me the feeling that it's trying to come up with hacks to shoehorn imperative UI React IS a hack to shoehorn declarative UI into an imperative language and DOM system. All further progress in the field is going to be a collection of hacks. ReasonML, Elm etc are clear ways forward that attempt to start from a clean slate, but you're trying to do declarative UI on JS+DOM, everything is going to be a hack. React is just the nicest currently available hack.
- jakelazaroff 8y agoBy this logic, though, everything declarative is a hack. Computers are imperative. At some point everything becomes an instruction; dig deep enough and all declarative code calls imperative code.
- deleted 8y ago[deleted]
- jasonkillian 8y agoWow, I really don't know what to think of this article. I have to give Dan credit that every time I thought the code started looking really crazy, he'd say something like: "Admittedly, [this] code can be disorienting. It’s mind-bending to mix opposite paradigms." And I think it's a good sign he recognizes the fair objections people will bring up. In addition to the paradigm-mixing the article illustrates, I wonder if the actual hook APIs make things more confusing than necessary at some points. For example, in the `useEffect` hook API: * the first argument is a function that on every render * the return value of the first argument is a "cleanup" function which runs when the component is unmounted or before the effect is run again * the third argument is an array of parameters which controls when the effect is run. If passed an empty array, the effect is only run at mount and cleaned up at unmount. All the above means that the API is very implicit and doesn't self-document its intent all that well. An example, from the article: useEffect(() => { function tick() { savedCallback.current(); } let id = setInterval(tick, 1000); return () => clearInterval(id); }, []); I'm sure with more use these hook patterns will become more familiar. But it's clearly a lot harder to learn and understand than the original simplicity that helped propel React to popularity. Dan's arguments that hooks provide for easier abstraction and more composability is interesting to me though - if hooks are going to be "worth it" in the long run, I suspect this is why.
- underwater 8y agoThe pattern of returning an unregister function is a common one. But I agree that the cache key array is strange. I'd almost suggest that a seperate useEffectOnce function would be better, but I'm sure the abstraction was heavily tested and bikeshedded before it was released in this form.
- feralfoo 8y agoYou could create userEffectOnce as a custom hook: function useEffectOnce(callback) { useEffect(() => { return callback() }, []) }
- Stoids 8y agoPerhaps I'm just a bit dumb here, but a few of these comments confused me. > However, this is a common source of mistakes if you’re not very familiar with JavaScript closures. > The problem is that useEffect captures the count from the first render. It is equal to 0. We never re-apply the effect so the closure in setInterval always references the count from the first render, and count + 1 is always 1. Oops! Perhaps I'm not understanding the second comment fully... This behavior is completely counter to how Javascript closures work in my mental model. I'm trying to figure out how a state hook differs in execution from a normal variable in module scope. How does it differ from this [1] example. [1] https://jsfiddle.net/xuz09cow/1/ https://jsfiddle.net/xuz09cow/1/
- jakelazaroff 8y agoThe difference is that closedVariable would be created inside intervalFn (the component). In addition, the intervalFn may run many times, but the interval will only be set up once. This example illustrates the problem: https://jsfiddle.net/ov2msa4e/ https://jsfiddle.net/ov2msa4e/ Your code avoids the closure problem, but closedVariable would be more like a static variable — it'd be shared by every instance of the hook. Probably not what you intended!
- Stoids 8y agoThank you for the example, Jake! It was very helpful. You and Jason's answer cleared it up for me.
- jasonkillian 8y agoThe difference is subtle (and took me a bit to figure out!). In the article's example component, the component's "main" function gets re-run on every render and a new `count` variable is created each time. The effect function is only run once at the very first render, and only captures the value from this first render. I'm having a little trouble explaining the difference well, I think this jsfiddle illustrates the incorrect case the article is talking about representatively: https://jsfiddle.net/7fLnvz5c/ https://jsfiddle.net/7fLnvz5c/
- daok 8y agoI read half the article and got lost. What I like about the "original" React is that it is simple to understand, close to JavaScript in a sense that you do not need to learn that much and be able to have a good result. I'm reading more and more about Hooks and still have a hard time to have a clear idea of how to avoid these pitfalls. At the point I am, it seems that Hooks has a lot of hype from few big influencers but that the day-to-day developers will be lost.
- bromuro 8y agoInstead of spending hours reading, start playing with them, it is fun and in few hours you will get why people are excited for hooks!
- Karupan 8y agoI do React at work and Elm for personal projects, and I feel the React ecosystem is trying too hard to become something its not. Hooks to me are an added level of complexity, and doesn't simplify the way I write or test components. I don't really mind the class based components as I feel there is less magic. Personally, React had a great run, but it's time to move on to a more purpose built language/framework.
- mharroun 8y agoMaybe I am crazy or old school but I though the concept of creating component as a class with standard functions to override (and defining a contract with props types) took a unwieldy language like JS and gave a sane and standard approach to it. I now look at more "modern" functional components /w hooks and find it VERY hard to understand the data a component takes and how it uses/responses to interactions. It feels like react was class based... and now that functional coding is the "in thing" its trying to shoehorn everything into functional components. In the end it really turns me off to functional programming as I see many developers sacrificing readable/understandable components to save a few lines of code.
- vmware505 8y agoI have the same feeling. I would prefer to use classes rather than functions. More natural and nicely work with Typescript. Luckily React is only a library and it is up to you how do you use it. I got used to working with classes and objects. For bigger and serious project I prefer Ember.js, which is modern and easy to pick up (https://yoember.com https://yoember.com), but for some tiny project React is also a good choice with Typescript. I guess hooks is a good option for devs who prefer functional programming.
- underwater 8y agoClasses aren't really that great in React. You typically need do a lot of juggling to co-ordinate lifecycle hooks. Trying to do something like register, update and unregister a listener feels very verbose with classes because you don't truely control the instance lifecycle – React does.
- WorldMaker 8y agoHooks work just fine in Typescript. Not just because Typescript already does will with Functional Programming, but because the types of most hook functions are generally rather simple. `declare function useState<T>(startState: T): [T, (nextState: T) => void]` is pretty simple function signature and most cases Typescript picks up (infers) that T generic parameter for you based on startState. Almost every other Hook function has a similarly simple type signature, despite the complicated "guts" of how React keeps track of hooks internally. (Which also doesn't seem all that complicated, relatively speaking.) (I still want a way to represent the order of effects / no effects in control flows rules in Typescript's type system, but linters will handle it fine. At this point it's mostly just a personal toy project/curiosity because Typescript feels like it has enough of the basic building blocks in the type system such as `never` to get quite close.)
- indeyets 8y agoMy issue with hooks is the learning curve for novices. Previously, it was always easy to have answers to "how does this work?" questions. Now, hooks give a lot of powerful ui l magic which lets programmers be super productive from day one, but we have a need for articles like this to describe things. It is not trivial anymore. A bit like rails-vs-sinatra story. React arrived to the magical stage and we already had ember for that. React was nice because there was no important magic. Ah… who cares? I still gonna use it. It just means I will have to hire more senior devs instead of juniors.
- danabramov 8y agoYour experience might be different but I’ve seen many beginners who struggled with classes pick up Hooks very quickly. A lot of the confusion has to do with unlearning a different mental model. Experienced programmers tend to get more confused by Hooks than beginners. Yes, there are pathological cases like this one. Somebody will put this useInterval on npm and then it won’t be a problem. I’m not convinced it be a big issue for beginners in practice based on my experience showing it to people and seeing them play with it.
- ryansolid 8y agoThere is some difficulty in understanding how this works at first if you haven't hit these problems before. But once you do, the payoff is immense. You think of problems in terms of describing how data changes over time rather than how am I going to make this display. The JSX part of the code becomes an afterthought. In the same way you aren't thinking about what DOM mutations React is actually doing behind the scenes, don't worry too much about how it wires together just describe what it does. Counter is a wonderful example. Look how contained and directed the hooks version is compared to jumbled mess the class version is. Picture that class component was anymore complicated, tracing through these lifecycle methods to figure out what is happening under any number of different input changes. Where the hook tells you in its definition when it updates. Each new behavior is just listed after and self-contained. Consider how refactoring and breaking apart components works in both scenarios. This paradigm is not new. New to React but not at all new to Frontend JS UI libraries. Ironically when presented in MobX, and KnockoutJS(way back in 2009) it was considered simple. Somehow in 2019 with how far we have progressed, it's too difficult?
- warent 8y agoCalling the class component a "jumbled mess" is nothing short of hyperbole. It could be simplified quite a bit however. The problem is an inappropriate use of "setInterval" here. If the author used a recursive "setTimeout" function instead, the entire "componentDidUpdate" function can be deleted. Alternatively, keep the setInterval and all the logic in componentDidUpdate could just be moved to the tick function. This contrived example hasn't sold me on hooks at all.
- mst 8y agoI kinda feel like this might be a lot simpler if you could use it mobx style, i.e. useEffect(state => { // reading state.count registers a dependency on the count }); but much though I've been enjoying mobx for model-ish things w/react I wonder if that would end up being too much spooky-action-at-a-distance
- inglor 8y agoThe funny thing is that with reactivity like Vue/MobX you don't actually _need_ to make setInterval (or any other function) declarative - the change detection system abstracts all this from you and you have one less concern to care about everywhere rather than having to do this manually in terms of hooks for every component. Everything is just JavaScript (well... TypeScript) and the abstraction is solid enough none of this is a concern you have to deal with. (I enjoyed reading the article - the writing was solid :))
- ryansolid 8y agoThat's true. In one sense perhaps hooks are trying to hard to model a different paradigm to what react runs on. But its interesting to see this approach as it applies this pattern to other change detection mechanisms not typically associated with fine grained change detection. Ive seen libraries apply hooks to like LitHTML in webcomponents. Its the first time Ive seen the potential for a universal API here. How sweet would it be if library writers could target hooks instead of a specific framework?
- underwater 8y agoCan you share an example? I'm not familiar with how you'd define a component-specific interval.
- mrccc 8y agoI found this talk from React Day Berlin quite helpful for understanding React hooks: https://www.youtube.com/watch?v=p8v9X2TNQKA https://www.youtube.com/watch?v=p8v9X2TNQKA (An Exploration of Reacts Exciting New Features - Ken Wheeler)
- panic 8y agoResetting the interval when the delay changes is probably not what you want either -- quickly changing the delay (even a little bit) won't give the timer a chance to fire. It would be better to compute a deadline internally, then move the deadline forward or backward according to how much the delay changed since the last call.
- zurfyx 8y agoA setInterval/setTimeout can only be cleared (clearInterval/clearTimeout). Modifications have to pushed through clear -> set.
- danabramov 8y agoGood thing something like useInterval() can abstract these implementation details away so it can be made more precise with time. :-)
- dakom 8y agoSeems to me that deps are to (some) hooks as props are to components?
- mst 8y agoThat's ... really not a bad first order approximation at all, and I'm going to steal it when trying to explain things to people. Thanks!
- crooked-v 8y agoI'll repeat what I've mentioned several times with things like this, which is that to me hooks just feel like a more convoluted reinvention of mixins.
- barryhoodlum 8y agoHooks are composable, mixins are not.
- elyobo 8y agoPet peeve: using `let` when you could use `const`. I've been conditioned to read `let` as a statement of intent to mutate that variable and it irks me when they then... don't.
- diegof79 8y agoPerhaps someone that knows more about hooks can help me to understand the trade-offs. After looking at the intro: https://reactjs.org/docs/hooks-overview.html https://reactjs.org/docs/hooks-overview.html My understanding is that each time that you call useState the renderer is keeping track of a state associated with the currently rendered component. It’s a clever solution, but that implicit context bothers me a little bit. For comparison, Flutter makes the state relationship explicit: https://docs.flutter.io/flutter/widgets/StatefulWidget-class.html https://docs.flutter.io/flutter/widgets/StatefulWidget-class... The Flutter approach is more verbose, and I don’t know if I like it or not. But the useState looks like a maintenance nightmare. There is no way to type that function usage correctly, and the dependency is not explicit (making refactoring and testing cumbersome). Any experiences to share?