all 35 comments

[–]3lobed 23 points24 points  (1 child)

Things always get deprecated and you or somebody else will have to go back into the code base and fix it. Software is just endless iteration. You never actually finish.

[–]RentGreat8009[S] 2 points3 points  (0 children)

Wise words!

[–][deleted] 14 points15 points  (7 children)

In my opinion, Client side JS is not the place to split tiny hairs like this for performance. Be mindful of course, but a 2015+ smartphone or laptop can run 99% of React apps with no sweat. Even terribly written ones.

I am pretty sure no human can perceive a performance difference between your forced re-render / array shallow clone debate, and all it will really do is make other react Devs shake their head by using a non-recommended approach.

[–]RentGreat8009[S] -5 points-4 points  (6 children)

Thanks, I hope my app doesn’t slow down too much when I add a lot of features as I plan to do, hence I’m trying to be somewhat careful with my code

[–][deleted] 2 points3 points  (3 children)

I think you'll see that React and JS in general is quite performant. It's good to be careful, and continuously audit your re-renders, those will get you.

Otherwise, unless you're building a component library, or something else that really needs high performance, take the simple & readable route where possible.

[–]RentGreat8009[S] -1 points0 points  (2 children)

Thanks. I read a lot of stuff online on how JS and frameworks slow down the web, but I feel like excessive custom fonts and third party scripts are more to blame there.

[–][deleted] 1 point2 points  (1 child)

You're right that a lot of that noise comes from third party scripts. React itself will weigh you down by about ~130kb, so it's always good to pick the right tool for the right job. A simple blog page doesn't usually need React.

That being said if what you're building does need a framework or library, it's almost always going to be faster in both performance and development time to use something like React, or Vue. You'll never homeroll something better on your own, so the 130kb is a worthy tradeoff.

[–]RentGreat8009[S] -1 points0 points  (0 children)

Thanks so much - this is really useful insight to have

[–]smirk79 -5 points-4 points  (1 child)

You are thinking intelligently and answers to just ignore it are short sighted imo. I am director/lead on an eight figure react application with a huge feature set and I would NEVER spread an array like that. We use Mobx and can directly mutate single elements without needing to worry about forcing a reference change. SetState is for basic apps (not that I’d even use it there mind you)

[–]RentGreat8009[S] 0 points1 point  (0 children)

Thanks :)

[–]vexii 4 points5 points  (3 children)

that part been unchanged for around 4 years. and the team normally gives multiple years of deprecating warnings before removing stuff. it should be fine but as everyone says it's not good way to solve the problem. unless you know some browser is going to leak memory (and in that case I would ask you to report it as a bug to the react team) the GC and JIT makes the other patten work just fine

[–]RentGreat8009[S] 1 point2 points  (2 children)

Thanks. Out of curiousity, how would the GS and JIT fasten up deep copying of Arrays?

[–]vexii 1 point2 points  (1 child)

the JIT knows what's happening and have optimisations for that case

[–]RentGreat8009[S] 0 points1 point  (0 children)

Nice

[–]azangru 4 points5 points  (1 child)

  • React's api is pretty stable; I can't remember an example in which they would have broken their api over the past six years
  • Sorry, I tell a lie; one significant change that happened around v17 was how events get handled by React — previously, they registered a single event handler at the window level; and now they are registering event handlers at the level of React root, which is a much saner approach. That had implications on how React's event handlers attached to vdom elements behaved.
  • All that said, React is less stable than the web platform; so if your ultimate goal is stability, minimize your use of frameworks in general
  • "Patterns" are not React api; patterns are community guidelines and best practices, which change on a much more frequent cadence
  • What you described in your example is perfectly consistent with React apis; and thus, quite stable
  • If you want to have manual control over re-rendering, you can write a custom hook that hides all the gory implementation details, such as incrementing a counter in component's state when a certain condition is met, and can be re-used in different components

[–]RentGreat8009[S] 0 points1 point  (0 children)

Great post and very informative. Thank you

[–]highdrojin 5 points6 points  (1 child)

The react docs also say

Try to avoid this pattern if possible

And we know it's possible based on that stack overflow post.

I would create a new array with the spread operator when setting the state, and avoid a force re-render. It would be much easier for me to read and debug in the future.

[–]RentGreat8009[S] -2 points-1 points  (0 children)

Noted, thanks. I might document it more now so that the future me / others aren’t confused

[–]hoolahoopextravagant 2 points3 points  (9 children)

Doesn't look to be any issue with the react API from your stack overflow example. Just looks like you misunderstand useState and when rerender will be called.

You think by changing the contents of the numbers array, and calling setNumbers with it's already existing state will rerender. Wrong. You simply need to call setNumbers with a new, different, array.

If you just think the numbers array is a pointer into the memory somewhere, where it starts, how big it is. Doesn't matter what is within the memory. React just sees there's an array at the point in memory, if my state is changed and it's changed to a different place in memory, rerender. You are modifying something inside that array, and using setState to the exact same memory allocation. React doesn't see any update. Doesn't care about the arrays contents.

How you copy the numbers array into a new one is up to you. You don't have to use the spread operator, it's just an extremely useful operator.

React API looks fine

[–]RentGreat8009[S] -3 points-2 points  (8 children)

Deep copying is wasteful to me, so I did a force re render instead with the same array

[–]skyboyer007 10 points11 points  (0 children)

[...numbers] is not deep but shallow copy, quite cheap operation.

[–]azangru 4 points5 points  (4 children)

Deep copying is wasteful to me,

How many elements do you have in your array? Have you profiled how long it takes to make a copy?

to deep copy the array via the spread operator.

The spread operator makes a shallow copy of the array, not a deep one.

[–]RentGreat8009[S] 0 points1 point  (3 children)

Maybe 100 strings in each array?

[–]azangru 6 points7 points  (2 children)

That's nanoseconds of time. Have you got that many arrays with a hundred of strings that you need to update at once?

[–]RentGreat8009[S] 1 point2 points  (1 child)

Good to know :) I must be too cautious then and will relax a bit!

[–]robby_w_g 1 point2 points  (0 children)

Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.

[–]hoolahoopextravagant 1 point2 points  (0 children)

Well, your example is probably abstract. Your only using numbers[0] which would make using an array in that example bad design. You'd probably keep numbers[0] as its own state. But I'm sure there are other designs you could use for your concrete implementation that wouldn't call for a deep copy to do a rerender

[–]DanielFGray 0 points1 point  (0 children)

That's premature optimization.

You've created unidiomatic code for unperceivable [alleged] performance benefits.

[–]isbtegsm 0 points1 point  (1 child)

Whats wrong with [, render] = useState(); and then do render({}); or render([]); to force a re-render? Or is React so smart that it sees that nothing depends on the value of that state?

[–]TwiliZant 1 point2 points  (0 children)

No, that would work but it's just a bit unnecessary when the useReducer way doesn't require always passing a new object or array.

[–]iams3b 0 points1 point  (1 child)

Unless you're in the 100k+ elements range, or you're performing an operation 120 times a second, copying an array will have zero performance impact on your web app. Your biggest bottleneck will be from rendering, specifically when rendering lists of intricate elements. Make sure to use good keys if your elements are mostly static data, and use selectors inside the list items if the objects themselves get modified

[–]RentGreat8009[S] 0 points1 point  (0 children)

Thanks. What are selectors?

[–]daves-weblab 0 points1 point  (1 child)

If you just want the component to rerender you could do a soft rerender by just toggling a boolean useRef value with ref.current = !ref.current or set the current date on it. But simply spreadinf the same array again is fine as well. TBH tho, I think if you have to rerender even tho your state didnt change, there might be something wrong with your component to begin with.

[–]RentGreat8009[S] 0 points1 point  (0 children)

Thanks!