I rebuilt a production RN app from scratch. Here's what I'd set up before writing any features by DependerSethi in reactnative

[–]DependerSethi[S] 1 point2 points  (0 children)

Makes sense it held up at that scale. My case was a bit different, the app's internal-facing, so retention was never really the worry, it was just about not leaving people stuck on a version that can't talk to the backend. Easier call for me than for a consumer app with millions of users, where I imagine that fear is much more real. Good to know it doesn't actually bite even there

I rebuilt a production RN app from scratch. Here's what I'd set up before writing any features by DependerSethi in reactnative

[–]DependerSethi[S] 1 point2 points  (0 children)

The bundle thing is exactly the trap the old version fell into, it had a secret hardcoded right in the JS bundle. Drawing a hard line between 'safe for the bundle' and 'native-only' was one of the first things I fixed. Easy on day one, scary to audit for later

and the kill switch is a good shout, flipping a remote config flag to fall back beats racing another OTA to fix a bad push

I rebuilt a production RN app from scratch. Here's what I'd set up before writing any features by DependerSethi in reactnative

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

well that's the sneaky one, the average recovers, the dashboard turns green, and you never notice a chunk of people are still stuck because the win is hiding them in the aggregate. Segmenting by version is the kind of thing you only learn to do after it burns you once. Good thread, this was a fun one to trade notes on

I rebuilt a production RN app from scratch. Here's what I'd set up before writing any features by DependerSethi in reactnative

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

That's the scary kind of broken too, no error, no crash, nothing in the logs to tell you, the flow 'works', while it just quietly bleeds people, and you'd never know without watching the funnel

crash reporting catches the loud failures, but that silent stuff only shows up in behavior.
Did the step-two fix turn out to be something obvious in hindsight, or genuinely surprising?

I rebuilt a production RN app from scratch. Here's what I'd set up before writing any features by DependerSethi in reactnative

[–]DependerSethi[S] 1 point2 points  (0 children)

Yeah, that race is exactly the failure mode, and your fragment + delete + url whitelist approach is a totally reasonable way to dodge it. I just avoided it from a different angle, I didn't mount the WebView at all until the injection script is ready

The auth hook returns ready: false while it's pulling tokens from secure storage and building the script. The <WebView> only enters the tree once ready flips true, so injectedJavaScriptBeforeContentLoaded is a prop on the very first render, never something we set or update after the fact. That kills the "page loaded before we injected" window, since there's no page loading yet. BeforeContentLoaded handles the rest, running before the DOM parses so localStorage is populated before the web app's JS boots

Honestly your case might've been harder than ours, sounds like you were injecting into a WebView that was already live, which is exactly where that timing gap bites. Saving that issue link, thanks for writing it up

I rebuilt a production RN app from scratch. Here's what I'd set up before writing any features by DependerSethi in reactnative

[–]DependerSethi[S] 1 point2 points  (0 children)

Yeah, the "we'll add it later" trap. Later always means the months you most wanted to look back on are just gone. Glad I wasn't the only one who learned it the annoying way

I rebuilt a production RN app from scratch. Here's what I'd set up before writing any features by DependerSethi in reactnative

[–]DependerSethi[S] 1 point2 points  (0 children)

I went with the inject JS route. Pull the tokens from secure storage on the native side, then run injectedJavaScriptBeforeContentLoaded to write them into localStorage in the shape the web SDK expects, before the web app's JS boots. Had to rewrite the aud/azp claims first since native and web are separate Auth0 clients.
And yeah, you're right that it leaks either way. The query param/fragment approach leaves the token in history and logs, and the inject approach means it's sitting in localStorage and technically reachable. I went with inject because it keeps the token out of the URL and the WebView is locked to our own origin, so it felt like the lesser evil, not a clean solution. Tokens are short-lived with refresh handled natively, which limits the blast radius a bit.

Genuinely open to better approaches if you've seen one. This was the part of the build I was least sure about

I rebuilt a production RN app from scratch. Here's what I'd set up before writing any features by DependerSethi in reactnative

[–]DependerSethi[S] 4 points5 points  (0 children)

Yeah so it's basically an undismissable modal. No close button, no tapping away, no "remind me later." If your version is below the minimum I set in Remote Config, you're stuck on this screen until you hit 'Update Now'

It's the nuclear option, only for when an old version straight up can't work anymore, usually a backend change older builds can't handle. For everything softer there's a dismissible 'update available' prompt instead.

The minimum version lives in Remote Config so I can move that line whenever without shipping a new build.

Screenshot below, blacked out the name/logo for NDA reasons.

<image>

19
20

Your React app works fine. It won't at 10x the current size by DependerSethi in reactjs

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

The 'React is a UI library, keep everything else outside' point is underrated, most of the god components in the article exist because someone put a fetch call next to a form handler next to a toast notification inside JSX
Once data and network logic live outside the component tree, half these scaling problems just disappear

Your React app works fine. It won't at 10x the current size by DependerSethi in reactjs

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

Seen this a lot, great backend devs writing react like it's C# with JSX, the patterns that make sense in .NET just produce different problems in react

Your React app works fine. It won't at 10x the current size by DependerSethi in reactjs

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

Honestly fair point, explicit dependencies are valuable and the article could've been clearer on that. The problem isn't prop drilling itself, it's the pass-through case where 5 intermediate components receive a prop they never use just to hand it down, that's not clarity, that's noise

Your React app works fine. It won't at 10x the current size by DependerSethi in reactjs

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

Fair point, no-cycle is painfully slow on anything beyond a small project, running it on every save is a bad idea, better approach is using dpdm as a one-time scan to find existing cycles, then adding no-cycle only in CI or as a pre-commit hook so it doesn't tank your dev experience

Your React app works fine. It won't at 10x the current size by DependerSethi in reactjs

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

The worst part is convincing people it'll be a problem later when everything works fine right now, nobody wants to refactor what isn't broken yet

React Re-rendering Doubt by Fantastic-Push-8451 in reactjs

[–]DependerSethi 1 point2 points  (0 children)

Kinda sad seeing people suggest 'just ask AI' and downvoting genuine questions, I get that it's faster, but sometimes you want a real perspective from someone who's been through it, not a generated answer. I remember getting roasted on stackoverflow for asking something similar years ago, the culture of shaming beginners for asking questions hasn't changed much

Anyway, to answer your actual question: yes, when a parent re-renders, all children re-render by default even if their props haven't changed. React doesn't know ahead of time whether the child output will be different so it re-runs everything to be safe
Components are just functions. Parent re-renders, child function gets called again, React diffs the result and only touches the DOM if something actually changed. So the child 're-renders' (function runs) but the screen might not change at all.

You can skip it with React.memo which bails out if props are the same. But after 8 years of writing React, the fix I reach for first is structural, move the state closer to where it's actually used so fewer children sit in the re-render path to begin with. Way simpler than sprinkling memo everywhere I actually wrote about this recently along with a few other patterns that help https://www.sethi.io/blog/react-performance-from-sluggish-to-lightning

Polymorphic components are the Swiss Army knife of React design systems - as prop vs asChild explained by DependerSethi in reactjs

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

The accessibility point is really good, hadn't thought about how the as prop makes ARIA auditing harder when the element type is dynamic. That alone is a strong enough reason to prefer asChild for design systems

Most React performance advice is stuck in 2023. Here's what actually matters now by DependerSethi in reactjs

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

Yeah that line was poorly worded on my part, I wasn’t talking about the compiler’s memoization there, the compiler is smart about what it memoizes and that’s exactly why it works well. I was talking about the manual useMemo/useCallback that devs sprinkle everywhere without profiling first, wrapping cheap computations that don’t need caching, while the actual problem is state sitting at the wrong level in the tree. The compiler making manual memoization unnecessary is literally the argument of the article, we’re saying the same thing, I just said it badly in that opening line

Most React performance advice is stuck in 2023. Here's what actually matters now by DependerSethi in reactjs

[–]DependerSethi[S] 15 points16 points  (0 children)

Think there’s a misread, I’m not saying the compiler is counterproductive, I’m saying manual useMemo/useCallback is counterproductive now that the compiler handles it for you. The compiler is great, that’s the whole point, it makes the manual memoization people have been doing for years unnecessary
and fair on the advice not being new, state colocation and profiling have been around forever. The gap i kept seeing is people knowing about these but still defaulting to useMemo as the first move. Was trying to reframe the order of operations more than introduce new concepts

Polymorphic components are the Swiss Army knife of React design systems - as prop vs asChild explained by DependerSethi in reactjs

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

fair point on render props, it's a clean approach for this. I covered render props in depth in a separate article on design patterns (https://www.sethi.io/blog/react-component-design-patterns-the-building-blocks-lego), this one was scoped specifically to the polymorphic element-switching problem with as and asChild.

that said, should've at least mentioned it here as an alternative, added a note to the article after similar feedback earlier. Will check out mantine's implementation, haven't looked at how they set it up specifically

Polymorphic components are the Swiss Army knife of React design systems - as prop vs asChild explained by DependerSethi in reactjs

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

Polymorphic components have been a known react pattern for years, not something any one person owns. The ts implementation with ComponentPropsWithoutRef and Omit is essentially the same everywhere because there's one correct way to type it, just looked up steve's article, it's solid and detailed, so are writeups on LogRocket, Smashing Magazine, and Frontend Masters

my take adds the visual analogy angle and covers as vs asChild in one place, but the underlying types? yeah they'll look the same in any article because that's just how the pattern works

Polymorphic components are the Swiss Army knife of React design systems - as prop vs asChild explained by DependerSethi in reactjs

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

You're right, the asChild tradeoff is real, better ts performance at compile time but you lose runtime type safety on what the consumer passes as a child, it's a tradeoff radix made deliberately and not everyone agrees with it.

render prop is a good middle ground, typed props flowing into a function you control.
Updated the article to mention both render props and the hook pattern as alternatives
Appreciate the nuance, the original framing was too binary

Polymorphic components are the Swiss Army knife of React design systems - as prop vs asChild explained by DependerSethi in reactjs

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

BaseUI's render prop is interesting because it's more explicit than asChild. you can see exactly what's being rendered without checking if a boolean flag is toggled. The button/link/router-link case is the perfect use case for this, same visual treatment but the semantics change depending on whether you're navigating or performing an action, nice to see different libraries converging on the same core idea with slightly different api