[HN Gopher] Common Beginner Mistakes with React
       ___________________________________________________________________
        
       Common Beginner Mistakes with React
        
       Author : vinnyglennon
       Score  : 55 points
       Date   : 2023-03-11 14:48 UTC (8 hours ago)
        
 (HTM) web link (www.joshwcomeau.com)
 (TXT) w3m dump (www.joshwcomeau.com)
        
       | petilon wrote:
       | One 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;        }
        
         | paulddraper wrote:
         | Yes, 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.
        
           | petilon wrote:
           | deepClone is convenient, when you want to modify a field
           | that's deep inside the object.
        
             | nicoburns wrote:
             | You should only be cloning the modified object, not every
             | single object in the array. That's what making things slow!
        
         | davely wrote:
         | Check 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 wrote:
           | Just read the docs for useReducer. It says you should not
           | change the original object. You have to make a copy.
        
         | mrkeen wrote:
         | Try going all in on persistent data structures. I haven't tried
         | this one but perhaps it will do the trick:
         | https://github.com/cronokirby/persistent-ts
        
           | petilon wrote:
           | I prefer to use POJO (plain old JavaScript objects).
        
             | phailhaus wrote:
             | You only need to update the objects that actually changed,
             | and only along the path of the keys that changed. Deep
             | cloning is overkill because you'll incur way too many
             | updates.
             | 
             | For example:                   const [largeArray,
             | setLargeArray] = useState<MyObj[]>([]);         const
             | updateObject = (index: number) => (newObj: MyObj) => {
             | setLargeArray(prevArray => ([
             | ...prevArray.slice(0, index),                 newObj,
             | ...prevArray.slice(index+1),             ]));         }
        
         | acemarke wrote:
         | Deep cloning is almost never the right answer - you'll end up
         | copying _too many_ object references, which is extra work, and
         | that can also lead to unnecessary re-renders depending on what
         | your components are trying to do.
         | 
         | A correct immutable update is more like a "nested shallow
         | update", copying all levels of nested keypaths leading to the
         | actual value you want to update.
         | 
         | If you prefer to avoid writing those immutable operations by
         | hand, Immer is a fantastic tool for simplifying the code by
         | using "mutating" syntax that gets turned into a safe and
         | correct immutable update:
         | 
         | - https://immerjs.github.io/immer/
         | 
         | - https://beta.reactjs.org/learn/updating-objects-in-
         | state#wri...
         | 
         | We even use Immer by default in Redux Toolkit:
         | 
         | - https://redux-toolkit.js.org/usage/immer-reducers
        
           | petilon wrote:
           | immer makes an "efficient copy" of the object, right? Keeping
           | references to some nested objects in the original object when
           | possible? If so that's cool... but not sure it is worth the
           | effort.
        
             | acemarke wrote:
             | Immer does a correct nested immutable update, same as if
             | you wrote the corresponding nested spread operations by
             | hand. It _only_ updates the nesting paths that led to the
             | value that got modified.
             | 
             | So yes, if you have a nested object with a bunch of
             | different fields, and you update `state.a.b.c = 123`, it
             | makes copies of `b`, `a`, and `state`, but preserves all
             | other nested references that are anywhere inside of
             | `state`.
             | 
             | It's the same as if you wrote:                   return {
             | ...state,           a: {             ...state.a,
             | b: {               ...state.a.b,               c: 123
             | }           }         }
             | 
             | but obviously much shorter and easier to read / maintain :)
             | 
             | It also conveniently eliminates the chance of a _real_
             | accidental mutation (which in the case of Redux was always
             | the #1 cause of bugs in Redux apps).
        
               | petilon wrote:
               | That's awesome! It lets me use POJO (Plain Old JavaScript
               | Objects), and it makes efficient copies? Definitely going
               | to give this a try.
        
             | [deleted]
        
         | bayesian_horse wrote:
         | This is what the "immutability-helper" library and immer are
         | used for. Or Immutablejs to go even more fancy.
         | 
         | It's not that expensive. You don't need to deep clone an array
         | to change one element, you just create a new array linking to
         | all the old elements, except the one you want to change plus
         | the new version. If you understand "referential equality", this
         | will make a lot more sense.
        
       | Nihilartikel wrote:
       | I 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
       | 
       | 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 wrote:
         | Depends 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...
        
       | localghost3000 wrote:
       | I'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.
        
         | phailhaus wrote:
         | I 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 wrote:
           | Right. 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 wrote:
             | Sorry, 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.
        
               | bayesian_horse wrote:
               | The last statement isn't true unless you mean
               | "mutability" in just the DOM elements.
               | 
               | All sorts of things can carry state and be mutable in a
               | React application. Even the properties of a component are
               | actually mutable, and it sometimes happens that people
               | pass around arrays or objects and mutate them when they
               | shouldn't.
        
               | phailhaus wrote:
               | Well, javascript is mutable so technically nothing is
               | immutable. But I'm referring to working with React's
               | hooks, where all props and state should be treated as
               | immutable except for refs.
        
               | bayesian_horse wrote:
               | Refs are just as immutable in that perspective. And no,
               | you're not supposed to mutate ref.current.anything
               | outside useEffect or a callback or similar...
        
               | phailhaus wrote:
               | Of course you can! That's the whole point of refs, and
               | why you can't use them as a dependency to callbacks or
               | effects. They're totally mutable, which makes them very
               | tricky to deal with. I mean, I wouldn't _recommend_
               | updating them outside of effects /callbacks, but it's
               | valid. For example: you can make a ref to count the
               | number of times React calls your render loop by updating
               | it in the body.
        
               | paulddraper wrote:
               | I 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.
        
               | bayesian_horse wrote:
               | Also: even with hooks, the component is still a pure
               | function in a less low-level kind of way.
               | 
               | The hooks yield effect descriptions, to be carried out
               | later, on a side channel. So the component is still a
               | pure map of the inputs to the outputs, just that the
               | outputs are not just comprised of the javascript
               | function's return value, but also the effects on the side
               | channel.
        
           | vlunkr wrote:
           | This is definitely true, but I also think it's overused in
           | typical UI components. I often see patterns where some user
           | event triggers a state change, which triggers a useEffect
           | hook. In most cases, you could instead have your event
           | handler directly trigger your side-effect code. Adding
           | useEffect into the mix is a huge point of failure because,
           | well, it sucks. Until you get that chain of deps just right
           | it's not going to run when you think it is, or it's going to
           | run with some stale values.
        
         | kkirsche wrote:
         | https://beta.reactjs.org/learn/you-might-not-need-an-effect
         | 
         | The beta react docs have a number of great examples of this!
        
         | wetpaws wrote:
         | Using functional components would be a mistake in general.
        
         | azangru wrote:
         | Calling useEffect an escape hatch is as much of a recent fad as
         | shitting on react. How can it be an escape hatch, when it is
         | the only api provided by react to perform side effects during
         | the rendering cycle, which do not themselves pertain to
         | rendering? Calling it an escape hatch is the same as calling
         | componentDidMount or componentDidUpdate methods of the class-
         | based api escape hatches.
        
         | hyperhello wrote:
         | I'm not against React but it sure seems like it's more
         | complicated than vanilla JS. The hater energy might be a clue
         | knocking at the door, trying to quietly inform you of something
         | you don't want to hear.
        
           | MatthiasPortzel wrote:
           | The people who work professionally with react all day are
           | well aware of React's problems. But I'm a front end
           | developer. I'm not about to change careers and become a
           | database administrator because managing state in React is
           | unintuitive. (And while there are other frontend frameworks
           | and solutions, they have their own problems, and it's more
           | difficult to find a job using them.)
        
           | phailhaus wrote:
           | Lol "quietly". React has been around for over ten years with
           | one of the largest grassroots ecosystems built around it.
           | Maybe that should be a clue that it has something to offer?
           | 
           | I think HN is averse to it because it's blamed for "how
           | complicated the web has become". If only we would all settle
           | for simple static websites!
        
             | hyperhello wrote:
             | It's not binary. There can be reasons to use it and reasons
             | not to.
        
           | blowski wrote:
           | Maybe, but it's more likely to be boring noise from immature
           | script kiddies who only want to use the newest tech because
           | it's cool.
        
       | threatofrain wrote:
       | If 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 wrote:
         | It'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 wrote:
           | Thanks 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 wrote:
             | React _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-...
        
       | watters wrote:
       | [flagged]
        
         | shrimp_emoji wrote:
         | [flagged]
        
         | waynesonfire wrote:
         | what 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
        
           | phailhaus wrote:
           | This is the HN bubble, which holds that React is "just a fad"
           | despite being one of the few libraries that have now lasted
           | for over a decade.
        
             | [deleted]
        
       | paulddraper wrote:
       | Great 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.
        
       | caxco93 wrote:
       | For 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)
        
         | chadlavi wrote:
         | This is also a much easier to read way of doing it.
         | 
         | For others' context, it's something like this:
         | setFoo((f) => f + 1);
        
       | jotaen wrote:
       | Regarding 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 wrote:
         | Right, 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-...
         | 
         | 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-stat...
         | which is unlikely to be what you want.
        
           | Swizec wrote:
           | > 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.
        
           | orangepanda wrote:
           | Its not an optimisation, its about state management.
           | 
           | React can't see the difference between a reorder and
           | remove&insert. When reordering items the state should be
           | moved as well; when removing and inserting a new item, state
           | should reset.
           | 
           | Using an array index is equivalent to silencing the error
        
         | suzzer99 wrote:
         | So what's the problem with using index then?
        
         | nicoburns wrote:
         | You're better off not using a key than using a random one
         | (unless the random key is kept stable accross re-renders)
        
       ___________________________________________________________________
       (page generated 2023-03-11 23:00 UTC)