30 ms·
Common Beginner Mistakes with React
- watters 4y ago[flagged]
- shrimp_emoji 4y ago[flagged]
- waynesonfire 4y agowhat would you recommend i install instead? well, great timing, "Ask HN: What's the fastest and simplest way to prototype a web app in 2023?" [0] is on the front page. It seems like every year the list changes. Seems like everyone uses react but nobody recommends it. Such a bizarre ecosystem. [0] https://news.ycombinator.com/item?id=35110976 https://news.ycombinator.com/item?id=35110976
- localghost3000 4y agoI'd like to add improper use of useEffect to this list. useEffect is an escape hatch but I often see devs using it as some kind of ad-hoc event driven system approach. useEffect divorces the outcome of an action from its invocation which is problematic. It can lead to some tricky state management scenarios really quickly. And as a side note: can we *please* stop shitting on every React post that appears on HN? All this hater energy is really starting to be a drag.
- 3np 4y agoThe last "solution" even commits this mistake. Blind leading the blind.
- phailhaus 4y agoI would say useEffect is not an escape hatch, it's a fundamental building block of the hooks paradigm. You need some way to run impure side effects after your component has been rendered, and `useEffect` is how you register those effects. The most basic example is making an external request based on props: can't do that in the render loop because it has to be pure, since React may run it multiple times before committing to the DOM. `useEffect` is the only way you can get a guarantee that it will only be run once after rendering, and only when its dependencies change.
- paulddraper 4y agoRight. It's how mutations are done. But there is a lot of mutable code that could be immutable, and `useEffect` is how that happens in React.
- phailhaus 4y agoSorry, what do you mean by mutable vs immutable code? I don't think it necessarily has to do with mutability, because you can "mutate state" in regular callbacks without useEffect. The only way to get true mutability in React is via refs, all other state is immutable.
- paulddraper 4y agoI mean pure vs impure. > you can "mutate state" in regular callbacks without useEffect Yes, via `useState`. That too gets overused. (Note: useEffect and useState are 100% necessary, but also easily overused.) useEffect for when you want to manipulate state outside the component. I.e. change the document title, exchange data with an HTTP server, store data in localStorage.
- threatofrain 4y agoIf I'm not mistaken, the issue with trying to read your writes is more about the fact that JS has no opportunity to update the variable until you run the function again: const [state, setState] = useHook("a") ... setState("b") console.log(state) I could imagine React adopting Signals and ending up with something like: const [accessor, setState] = useSignal("a") ... setState("b") console.log(accessor())
- phailhaus 4y agoIt's not about JS, it's because React won't let you render an inconsistent view of state. `setState` allows it to queue up multiple state changes, commit them all, and only then re-render your component. So you should think of `setState` as "queuing up a change to be applied for the next render cycle".
- threatofrain 4y agoThanks for the clarification. I had thought that React updates state immediately and may preform multiple re-renders prior to committing to the DOM, and it's the re-renders which are queued. I wonder how Preact works with Signals, then?
- acemarke 4y agoReact _usually_ queues up a render, which is then executed at the end of the event loop tick. This allows many calls to `setState()` in the same tick to result in a single render pass at the end. The default behavior is that _if_ there are any changes that are needed based on the render, React then continues ahead and applies them to the DOM, and most of the time that is done immediately. When a render is queued, React keeps the info about the requested state update as part of the queue, and those state changes are calculated and applied as part of the "render phase". So, if you happen to call `setState(2); setState(3);`, the second call completely overrides the first and there won't even be an attempt to render the component using the `2` value. (You can use the `setState(prevValue => 3)` form to force React to do each value separately.) As of React 18, the new "concurrent rendering" features like `startTransition` and Suspense allow React to alter priorities of renders, and split the calculation into multiple chunks over time with pauses in between. When React _is_ done determining any changes in that render pass, it still applies them to the DOM in a single synchronous "commit phase". The other caveat is that React will sometimes execute a full render+commit synchronously under specific conditions, such as a `setState` inside of a `useLayoutEffect` hook. I wrote an extensive detailed post called "A (Mostly) Complete Guide to React Rendering Behavior" that tries to explain a lot of these nuances: - https://blog.isquaredsoftware.com/2020/05/blogged-answers-a-mostly-complete-guide-to-react-rendering-behavior/ https://blog.isquaredsoftware.com/2020/05/blogged-answers-a-...
- caxco93 4y agoFor the examples regarding Mutating state and Accessing state after changing it I believe it would've been a great chance to teach about "functional updates" on the `set` functions, which can be accomplished by passing a function as an argument instead of the `nextState` value. (ref https://beta.reactjs.org/reference/react/useState#setstate https://beta.reactjs.org/reference/react/useState#setstate)
- chadlavi 4y agoThis is also a much easier to read way of doing it. For others' context, it's something like this: setFoo((f) => f + 1);
- Nihilartikel 4y agoI love React as long as it has a thin skim of clojurescript over top. Rum is the underdog compared to reagent but is still my weapon of choice - https://github.com/tonsky/rum https://github.com/tonsky/rum Was disillusioned when I had to dive into a pure js project using React. The real benefit, I think, is that you get the well established Clojure idioms around isolating and managing mutable state. State is stored in a Atom, which is atomically mutated, and reactive components essentially 'subscribe' to updates upon that atom to re render. The mutations can be handled centrally by a message queue, but really, event sourcing like that is not always needed.
- bayesian_horse 4y agoDepends on the perspective. I know a bit of Clojure, but for me those idioms are hardly "well established". For me, the patterns of React, Typescript and Redux are well established...
- dgb23 4y agoI believe these functional React and Redux patterns got largely inspired by the Clojure ecosystem. It took a while for the adoption of functional patterns in the JS world. But I believe what OP meant is really more about how these idioms are holistically implemented throughout Clojure. Whereas in JS we have to fight the language quite often.
- bayesian_horse 4y agoFunctional programming is older than Clojure. Wayyyy older.
- paulddraper 4y agoGreat list! > Missing whitespace. I've since realized that there is no perfect solution to this problem. Indeed :) HTML had the same problem, albeit with different and equally frustrating choices. The problem is inherent to markup languages: difficulty separating intentional content from internal formatting. That is the both the feature and bug of markup.
- petilon 4y agoOne of the biggest issues in React that I have come across is that you have to make copies of objects and arrays (as mentioned in this article) when calling setState(). Cloning works well for small arrays and objects (I use the code below for that), but for large arrays with thousands of items (for example, datagrid scenario) this is inefficient. My solution is to modify the array/object directly then just call setState({}). Does anyone have a better solution? export function deepClone<T>(obj: T): T { if (obj == null || typeof obj !== 'object') return obj; const clone = new (obj as any).constructor(); for (let key in obj) { if (obj.hasOwnProperty(key)) clone[key] = deepClone(obj[key]); } return clone; }
- davely 4y agoCheck out useReducer and all your problems go away! (It has no relation to Redux, many people think this but it’s a native React hook that makes updating keys within an object much easier)
- petilon 4y agoJust read the docs for useReducer. It says you should not change the original object. You have to make a copy.
- paulddraper 4y agoYes, that's the best way. Fortunately, there are relatively few components that need a hack like that. PS Why did you post code a `deepClone` function? You certainly don't need to deep clone.
- jotaen 4y agoRegarding the usage of `crypto.randomUUID()` to generate unique `key` properties for list items, such a technique should only be necessary if the items don’t come with unique and stable ids on their own. This is the case for the example in the article, but in my experience, list items often do have some differentiator on their own, which you can either use directly, or otherwise derive for that purpose. If that’s the case, this is what I usually would prefer by default. One other aspect that I find important to understand about the `key` property is that it only has to be unique among its direct siblings, but it doesn’t have to be unique globally.
- mediumdeviation 4y agoRight, I'm not sure the author understands key either. It's an optimization to help React identify insertion / removal / reordering of array items between re-render. https://beta.reactjs.org/learn/rendering-lists#where-to-get-your-key https://beta.reactjs.org/learn/rendering-lists#where-to-get-... Giving every item a new key on every update does not help with that - in fact it is likely strictly worse than just using index since it will cause every item to re-mount every update https://beta.reactjs.org/learn/preserving-and-resetting-state#option-2-resetting-state-with-a-key https://beta.reactjs.org/learn/preserving-and-resetting-stat... which is unlikely to be what you want.
- Swizec 4y ago> Giving every item a new key on every update does not help with that fwiw the author specifies says not to do this. The advice is to generate a unique ID when items are created, not when they’re rendered.
- isleyaardvark 4y agoI was going to reply the same thing. I do have a problem with the author presenting that as a default solution instead of a workaround if the item doesn't have a unique identifier, which I have found to be uncommon.
- orangepanda 4y ago
- react_noob 4y agoI don't use react but... isn't his solution also wrong? function handleAddItem(value) { const nextItems = [...items, value]; setItems(nextItems); } should be? function handleAddItem(value) { setItems(items => [...items, value]); }
- reducesuffering 4y agoNo, you can provide the state setter, setItems, with a new copy of the state object itself. Doesn't need to be a function.
- simple-thoughts 4y agoBoth are valid, but you’re right in that, at least in my experience, the second option can be safer. Specifically when updating an object or list via an asynchronous call where the calls can change based on state (such as the user changing accounts)
- jameshart 4y agoIf there are no other ways to update that state, the example will work fine. It might come back and bite you if you implement something that performs an async update of that state via a different path later.
- erikpukinskis 4y agoIn my experience, that form of useState is an anti-pattern… It’s not that it doesn’t work, it does. But I’ve found there are basically two scenarios: 1) Simple cases where there’s no risk of contention and you can just operate as if your state variable is up-to-date. 2) Complex cases where you have state changes coming from multiple directions and contention is a concern. In the second scenario I just go straight to useReducer. It is much more reliable than useState when there is the possibility of contention. And it’s much easier to reason about than having a bunch of setState calls all over. IMO the setState((oldState) => newState) pattern is a half measure that’s almost never the best choice.
- throwaway290 4y ago
- wruza 4y agoIt became so steadily imprinted into minds of developers that very few realize how nuts it all is. Back in 2000 nobody in their mind would take seriously these perverted constructs loaded with footguns to create a form. And even if you are aware of what’s going on, this thing itself struggles to solve trivial tasks in non-twisted ways. Sigh.
- plantwallshoe 4y agoCreating all but the most basic of forms with plain html/css/js is loaded with footguns too. Add in conditional fields, multi-page flows, loading and error states, etc then it becomes clear why solutions like React were created and why they have become so popular.
- Escapado 4y agoI could not agree more. At my current gig we have forms spanning 6 pages, where the state of each sub-form needs to be preserved, pretty much half of the form fields are conditionally loaded across all pages. There is tons of dynamic data displayed and sometimes that needs to be transformed client side, has loading and error states and there is heaps of validation happening, some of it asynchronously and it involves calling other services. Some of the inputs should be also preserved for other parts of the page regardless of whether the user fully completes all forms or not and then there is a whole basket of custom tracking that the marketing team insists needs to happen. You could of course argue that this is bad UX or that the problem starts with the requirements but that’s usually not something engineers a lot of have control over so I am actually quiet grateful that we have a framework that lets me solve this (imho) more sanely than if I were to hack away in vanilla JS.
- infamia 4y agoIt is very true that the types of situations you mentioned are very painful with just HTML/CSS/Vanilla JS. However, React is no longer the only game in town for the things you mentioned. LowJS frameworks like Unpoly with a good server side rendering framework can handle the things you've described and a lot more. For example, you can use Unpoly with a server-side framework to allow layers to either act independently or to cascade information down to lower layers for multi-page/modal window flows that need to share information all without full page reloads. [1] Using layers you could build some pretty decent sized apps, Unpoly gives an example [1] showing (conceptually) how you could build large parts of Gmail with layers. You can also do things like dynamic forms with a bit of code on the back end. You get all this without needing to keep state in sync between the front end and the back end. There will be cases where Unpoly (which can do quite a lot) only does 95% (or whatever) of your requirements. In those cases you can sprinkle in a bit of Vanilla JS to help fill the gap. Additionally, there will be some cases where Unpoly isn't at all suitable, like if you're building something that is extremely interactive (i.e. the next Google Docs/Maps or similar). However, those cases keep getting smaller as these LowJS Frameworks improve. Everyone's case is a very particular set of circumstances and this might not apply at all to your situation, but others are finding success with using HTML over the wire types of approaches. [3] One final thing to note that's often overlooked is that CSS' capabilities are growing, and can do a more than in the past. All things to think about before reflexively defaulting to React. [1] https://unpoly.com/up.layer https://unpoly.com/up.layer [2] https://unpoly.com/tutorial https://unpoly.com/tutorial [3] https://htmx.org/essays/a-real-world-react-to-htmx-port/ https://htmx.org/essays/a-real-world-react-to-htmx-port/
- dajonker 4y agoThese "mistakes" are presented as React specific but I think most of them are general programming mistakes. Or perhaps JavaScript mistakes if your please, since not all languages allow you to make all of the mistakes with mutating and using state. For example the "Accessing state after changing it" has absolutely nothing to do with React or async code, and everything to do with the scope of the count variable and how JavaScript passes numbers by value.
- jagged-chisel 4y agoWithout React (or a similar framework) JavaScript makes the assignment immediately. It’s not like the language has some special handling compared to other languages in this regard. React, otoh, provides methods that access and update state. React’s handling of changing state using these methods is to queue the change, not to update it immediately.
- dajonker 4y agoI'm talking about this specific piece of code: function handleClick() { setCount(count + 1); console.log({ count }); } Since count is enclosed within the function, it could be mutated by _anything_. In isolation, you can't really be sure of what setCount does, since the "count + 1" statement passes a completely new value unrelated to count. If setCount didn't have any side effects, it is an obvious mistake to assume the value of "count" would have changed. So if the intention is to log the incremented count, it is bad code in any situation, React or not.
- jagged-chisel 4y agoWithout further context, that looks like a property setter. There’s absolutely nothing wrong with thinking that the property setter sets the property immediately. What you’re saying amounts to “functions/methods can do whatever they want, and assuming they do what they claim to do is an obvious mistake.”
- activitypea 4y agoHow would you advise someone learn frontend development with React? The React docs are phenomenal, but they're just docs. I'm looking for something more structured, but most of the books I've found seem kinda cobbled together and low quality
- Fellshard 4y agoComeau (the author of this post) has beta-published a React course, so keep your eyes peeled.
- dagurp 4y agoHis CSS course is fantastic so I think I can safely recommend this one too.
- yubiox 4y agoI enjoyed the Wes Bos classes. They are not free. https://reactforbeginners.com/ https://reactforbeginners.com/
- pid-1 4y agofrontendmasters.com has a really great library of courses, and you can watch the introductory ones for free.
- reducesuffering 4y agoThis Full Stack Open course from University of Helsinki Finland is phenomenal. https://fullstackopen.com/en/part1 https://fullstackopen.com/en/part1 full syllabus here: https://fullstackopen.com/en/#course-contents https://fullstackopen.com/en/#course-contents
- ronnier 4y agoI stopped guys, I stopped doing it. For internal tools at my company, I just use a golang backend (because most of our services are golang so there's no learning curve for folks) and use go's templating system to render HTML. I can crank out tools in minutes. No complicated build systems or web pack or dependency management system. No react, no reduce, no apollo or graphql. No typescript, etc. Just simple go, html, css, and a bit of javascript when it's needed for a form. I don't minimize anything, or try to do anything fancy. It faster to develop in, and faster to load in the browser. I'm specifically talking about internal tools here.
- loeber 4y agoAgreed. I like this blog post on the topic, from a startup moving from React to Vanilla JS and HTML: https://www.getwaitlist.com/blog/react-to-vanillajs https://www.getwaitlist.com/blog/react-to-vanillajs
- lprd 4y agoThis is the way, and should honestly be the default unless you are 100% your software needs something like an SPA.
- revskill 4y agoThat small "unless" might require a full rewrite your application.
- zelphirkalt 4y agoWell, it does require some though about SPA or website upfront, but will save lots of work and time to being online. I guess we can at least demand that much thought about things, instead of jumping immediately to SPA and causing weeks and months of more work. I have seen it. It is all "But React has this built in!" and "React does this nicely!" until it comes to actually implementing components. Without a need for interactive widgets to avoid page reloads and avoiding becoming a non-SPA again, it will take a lot more time to get things done with React, that it would take to simply churn out server-side rendered templates the traditional way. UIs that could have been done in 2 weeks the traditional way take suddenly multiple frontend devs a few months, with things like the back button not working correctly.
- _hcuq 4y agoUsing it in the first place? :) Cue the downvotes....
- paulryanrogers 4y agoCare to elaborate?
- rk06 4y agoOPis saying that you should not use react in first place
- paulryanrogers 4y agoClearly but why?
- rk06 4y agoIt is just a joke. React is most popular, so it is the most risk averse choice. Vue is better in many technical areas, but there is not that popular. So that is a trade off that you need to consider. And many business would prefer to be risk averse
- peterkelly 4y agoReact was a great idea; the problem was they did it in JavaScript instead of a pure, functional language. There's too many footguns where JS features like mutability and async code can leak through, leading to a whole class of errors that could have been caught by a compiler but instead were left as cognitive load imposed on developers.
- jakelazaroff 4y agoThe (lack of) popularity of Elm and ReasonML are probably instructive as to how well React would have done in a pure functional language.
- kevincox 4y agoThe really subtle footgun that I see is unexpected state preservation. This is where the component isn't designed to be "reused" for completely different props. This is because the state has something about the props and will end up unhappy if they change. Some common warning signs are generic parameters for components and using a prop as the default value for a state hook. But neither of these are a reliable indicator of a problem. These bugs also often lie hidden for a long time because They can be hard to trigger because you need the same structure of components with a generic property changed. But when they bite they seem like impossible errors. I've wondered if React could add a useStateKey([deps]) hook that would allow forcing a state reset if the given values change. This would work similar to a key attribute but be controllable by the code inside the component.
- paulddraper 4y agoI've seen this problem often, and not just in React. I agree
- quickthrower2 4y agoThis follows on from using props as an initializer - something that should be avoided if possible. Date pickers get gnarly here: is the date prop the initial date or the current one? I think it should be the current one, with a prop to handle date change.
- munchbunny 4y agoThis was more explicit when implementing class based components than functional components because the class based API had explicit overridable methods for those aspects of the component lifecycle. That issue probably wouldn’t exist except that React has to make some concessions to performance, therefore component reuse, therefore component state working in sometimes unintuitive ways.
- 3np 4y agoI think I've seen all of these at some point (: Unless I'm missing something, I think the "Async effect function" solution is not correct: React.useEffect(() => { async function runEffect() { // Effect logic here } runEffect(); return () => { // Cleanup logic here } }, [userId]); This looks like a race-condition where cleanup may be done before `runEffect` has finishes resolving, and any errors thrown in `runEffect` will be unhandled promise rejections.
- wokwokwok 4y agoThis should be number 1 on the list, and it's wrong. Specifically: React.useEffect(() => { // Create an async function... async function runEffect() { const url = `${API}/get-profile?id=${userId}`; const res = await fetch(url); const json = await res.json(); <-- OK setUser(json); <--- Wrong. } ``` This is, hands down, the #1 mistake that react developers make. I've seen it literally in every react code base I've ever opened (if the code base had any kind of `fetch` in it). It's covered in the documentation now, explicitly: https://beta.reactjs.org/learn/synchronizing-with-effects#fetching-data https://beta.reactjs.org/learn/synchronizing-with-effects#fe... This is a prime example of a sharp edge with react hooks; it's a very very common pattern, but because people don't understand what's going on, they do it wrong naively; and even when you do know it's wrong, it's a rare edge-case that causes an invisible bug ("Tried to update a component that wasn't mounted...") and makes code messy and hard to read. The tldr should be: Do not use async effects. Use suspense.
- creamyhorror 4y agoThe section in the beta docs you linked to shows an async function startFetching() which does setTodos(json). How does that reconcile with you saying that setUser(json) is wrong?
- crooked-v 4y ago> Do not use async effects. Use suspense. I have to wonder how much pain would have been saved if the React team had just shipped a `usePromise` hook years ago.
- mgraczyk 4y agoIt may be trite to even mention it at this point, but for fun I gave all the example code in the article to ChatGPT and it correctly explained the problem in all but one case (the whitespace one). Pretty cool!
- throwaway290 4y agoHow do you think ChatGPT did it? Thanks to people who wrote articles like this. Which then OpenAI shamelessly scraped So by me not so much trite as faux pas.
- ryanmcbride 4y agoI just finished reading and I've run into every single problem listed here at some point in my career. These are all really solid tips, and it's also kind of validating to see that some of the things that tripped me up in the past are apparently pretty common tripping points.
- h4ch1 4y agoI'll be a Svelte evangelist till the day I die, I avoided the whole early reactivity phase because of how off-putting React was, finally started when Vue became slightly stable, and completely switched to Svelte when ractive.js was being sunsetted and Sapper/Svelte was the new thing in town. Haven't looked back since. Even now I really think half the reason everybody jumps on the React bandwagon is because everybody is using it for some inconcievable reason. Heck they won't even look at Solid or Svelte. React state management honestly looks like it was tailor made to torture you. Reminds me of how painful RecyclerView used to be before Compose.
- fulafel 4y agoMost (7 out of 9) of these don't happen when using React from ClojureScript via the most common way, Reagent: "Evaluating with zero" doesn't apply because you use Reagent's builtin Hiccup functionality instead of JSX and you have no temptation to use anything like these single-js-expression code patterns because, well, there's no statement/expression divide in Clojure. "Mutating state" doesn't happen because you don't mutate things anyway. "Not generating keys" does happen. "Missing whitespace" does happen (I think?) "Accessing state after changing it" - Nobody uses state hooks as it's just worse than the normal atoms way, although there's a way to do it. "Returning multiple elements" doesn't happen since, well, you can return multiple elements, components return vectors (Hiccup is vectors based data structure). "Flipping from uncontrolled to controlled" again is state hook specific, which are practically unused. "Missing style brackets" is JSX specific, doesn't happen in Hiccup. "Async effect function" doesn't happen. (The story is mostly the same with Reagent alternatives too)
- Naomarik 4y agoMy personal experience is that using reagent is so intuitive that I had launched a non trivial form to post ads on a production site within a week not knowing both clojure/script and anything about react.
- jbverschoor 4y agoGood list of reasons why React still is pretty shit.. Most if not all things could've thrown proper errors explaining what's happening.
- nh2 4y agoI like the post, it goes over multiple issues I had trouble with when learning React. Another post I found insightful is this one, translated from a post by the author of the Alibaba Hooks library: https://enlear.academy/5-things-i-disagree-with-react-hooks-offical-documentation-676d67185b14 https://enlear.academy/5-things-i-disagree-with-react-hooks-... It is also quite useful to check https://ahooks.js.org/hooks/ https://ahooks.js.org/hooks/ directly: Observing that there are hooks for seemingly basic tasks, and reading their linked code, makes it easier to figure out more React anti-patterns, simply by seeing what the hooks author did instead. I really wish the official documentation was better. Often the docs are self-referential, referring to how current approaches are better than thing X React did in the past, and then the trail gets lost, instead of explaining from first principles how things work and why they are done the current way.
- makingstuffs 4y agoCorrect me if I am wrong but wouldn’t the best solution to the “Accessing state after changing it” problem be to pass a callback as the 2nd arg to the “setState” function? https://beta.reactjs.org/reference/react/Component#setstate https://beta.reactjs.org/reference/react/Component#setstate
- black_13 4y ago[dead]
- _bax 4y agoReact (or similar) is ok for page components, not for the whole portal