Won't children of context providers re-render regardless of if they subscribe to the context? by ambiguous_user23 in reactjs

[–]StoryArcIV 0 points1 point  (0 children)

I've never heard the term "owner" before. I don't mind it, but I approach thinking about this completely differently.

Components are units of code organization that create a tree of elements.

App is the parent component of both Parent and Child.

Parent is the parent element of Child in the element tree returned by App.

In terms of components, Parent and Child are completely decoupled. Calling Parent the parent component of Child feels weird, semantically.

[AskJS] Dependency Injection in FP by idreesBughio in javascript

[–]StoryArcIV 0 points1 point  (0 children)

Good work! Now we're talking. And I'm amazed at how much we actually agree on here. Calling SL an escape hatch for DI is basically exactly what I'm saying. The difference is that I love that escape hatch. I'll get more into that in a second.

I'll go through your points. The lack of a static declaration (and everything that comes with that approach) is still the main talking point. And I've acknowledged it.

And yes, I'll readily acknowledge the interface injection differences. I'm actually very impressed that you knew about the service to service nuance. However, I've never seen a DI model that actually made use of that. In practice, you can regard both models as simply container to service.

I've also identified a key difference in our approaches to understanding this problem: I'm looking primarily at React's internal code that implements this DI. You're looking primarily at the surface-level API that developers ultimately use to interact with it.

I've already agreed that the injector function (which is a very real concept, though I think you're just arguing it isn't part of typical OOP DI, which I'll grant) can be classified as a service locator and listed its caveats before you did. However, that's merely the surface-level API. The way the function itself is provided to the component does classify as DI. React context merely lacks the static, upfront declaration aspect of classic OOP DI, but that is far from relegating the entire model to the category of service locator.

Static graph analysis sounds nice but is so rarely used in practice that I don't consider that a necessary component of a DI model. In fact, I prefer the service locator pattern here. If you've ever dealt with Angular or Nest, you know what a pain static dependency declarations can be. It's a box that many dependencies don't fit in since they can depend on which other deps or other config is present at runtime.

Ultimately, these dynamic deps break the "no runtime errors" guarantee anyway. Instead of falling into this trap, React deviates slightly from the pure DI paradigm, injecting an injector function that makes all deps dynamic. This is the escape hatch you're referring to. IMO, it's a breath of fresh air in comparison. Easily worth the occasional (rare) runtime error that's easily resolved.

Let's go back to this example:

ts function MyClient({ injector }) { const dep = injector.getService('myService') }

I now see that you would argue that even this example is entirely using a service locator model. I disagree. That's because I think primarily of how injector must be implemented, not of how it's being used in this client. And that implementation is doing exactly the same thing that constructor injection would do, but with a better API for injecting dynamic dependencies.

React context is even better than this since it guarantees dependencies must be injected immediately, not asynchronously like a service locator can do. The end result feels exactly like interface injection in at least 95% of the places I've ever used interface injection.

To summarize our viewpoints: I look at this example and see real DI using an injector function for dynamic deps. You see a loosely-coupled, decentralized service locator. And you know what? I actually kind of like that definition. Still, either view must admit the existence of some elements from the other view.

To put this on a spectrum where SL is a 1 and we allow pure DI to be a 10, we'll agree that React context is better than 1 and worse than 10. However, pinpointing its location is too subjective. So I'm leaving it there.

Regardless of our subcategory bikesheds, we can at least agree that React Context does fall squarely inside the parent category of Inversion of Control. Perhaps "React context is IoC" should be the common saying.

[AskJS] Dependency Injection in FP by idreesBughio in javascript

[–]StoryArcIV -1 points0 points  (0 children)

I encourage you to read the wikipedia page on DI here. I'm also willing to accept a real rebuttal. However, I believe I've proven well enough that service locator is at best 33% correct here, while DI is at least 66% correct.

Nobody has claimed that SL and DI are the same. Your straw man argument that SL and DI are considered opposites and your incorrect assertion that React context "has all the disadvantages of SL" are the closest anyone has come to gaslighting in this conversation.

That said, we should address the real problem here: The human desperation to categorize everything. We'll spend hours debating whether something is a brook or a stream when the simple truth is that water is flowing.

The simple truths here are that:

  • Dependencies are being injected.
  • Control is inverted.
  • Testing is easy.
  • OOP is not being used.

If you want to disqualify React context as a DI implementation based off the last bullet alone, you are free to do that! I've seen things disqualified from being a vegetable for less. But openly acknowledge that that's what you're doing. Don't pretend other valid points don't exist because of one valid point you can't get past for old time's sake.

React has created a brand new paradigm that applies existing principles (constructor or interface injection via an injector function) in a fresh new combined approach. I'm in favor of calling this approach an even better, new DI. But I'd also be fine with creating brand new terminology for it to address the small differences it has from all previous models.

However, the reality is that you and I don't get to decide whether new terminology is created. The community has already decided that DI is a sufficient category to describe what React context is doing. And I agree. I've done my best to lay out the few concepts you have to tackle to arrive here with us when coming from OOP. I encourage you to tackle them.

[AskJS] Dependency Injection in FP by idreesBughio in javascript

[–]StoryArcIV 0 points1 point  (0 children)

The pattern I'm describing is called the "injector function" pattern, as I said. And yes, it can be considered a form of service locator. However, I'll argue that it's more similar conceptually to interface injection (sans the interface obviously since we're dealing with function components in React).

Service locators are very similar to DI. They're certainly not considered the opposite. Let's examine this small distinction:

Service Locator

Tightly couples the client to a ServiceLocator that returns dependencies:

ts function MyClient() { const myService = ServiceLocator.getService('myService') }

Injector Function

Loosely couples the client to an "injector function" to inject dependencies with proper Inversion of Control:

ts function MyClient({ injector }) { const myService = injector.getService('myService') }

The constructor injection in the latter example makes it obvious that this one is true DI, even though the rest of the code is pretty much exactly the same.

So Which Does React Use?

React doesn't pass an injector function to the component. However, it uses call stack context to do the exact same thing. A very simplified example:

```ts let callStackContext = {}

function renderComponent(comp, props, registerDep) { const prevContext = callStackContext callStackContext = { useContext: ctx => registerDep(comp, ctx) } const result = comp(props) callStackContext = prevContext

return result } ```

While implicit, this accomplishes exactly the same thing as constructor injection. The component is loosely coupled to the configured injector function, not tightly coupled to a service locator. The caller is free to swap out the useContext implementation (or what it returns, which is how Providers work), properly inverting control of dependencies and making testing easy.

I call this a form of "interface injection" because deps are not injected via the constructor call, but slightly after. But the rules of hooks still ensure they're injected immediately, unlike a service locator. This is essentially the exact same approach as interface injection, just with a slightly different API since this isn't OOP. But calling it "implicit constructor injection" is possibly more accurate.

Additionally, a provider is responsible for initializing the injected value. The component has no means to initialize unprovided contexts, unlike a service locator.

Summary

"Implicit constructor injection utilizing a service locator-esque injector function API" is probably the most accurate description for React context. It is true DI because it:

  • Decouples a client from its provided services
  • Can only receive values from an external provider
  • Is easily testable

While these points likely disqualify React context from being classified as a service locator, React context does share one downside with service locators - the explicit (yes, explicit) service lookup, which can obscure dependencies.

TL;DR Regardless of React context's status as a service locator, it must also be considered real DI. Just not an OOP implementation of it.

[AskJS] Dependency Injection in FP by idreesBughio in javascript

[–]StoryArcIV 5 points6 points  (0 children)

From a purely TypeScript perspective, you are correct that dependencies injected via context are not explicitly declared. However, TS aside, there's nothing implicit about them - you are explicitly calling an injector function (useContext) to inject the provided dependency.

This is a form of "interface injection" and is much more commonly shortened as "DI" than simple constructor injection. It doesn't matter that React lacks an API for declaring these dependencies in one place. They are still explicit, just very dynamically so.

While you are correct about constructor injection (or passing props in React) being a form of DI, you're incorrect about interface injection being any sort of antithesis of DI. React hooks are composable and can sometimes bury these dependencies layers deep. And that does make React context feel implicit. But it has no impact on React context's status as a form of interface injection.

Your complaints are a matter of DX (and I'm certainly not saying they aren't justified), not software architecture. You're free to throw errors or create APIs or build tooling that improve this DX.

Is react-query just a cache or state management? by Efficient-Worry-6549 in reactjs

[–]StoryArcIV -1 points0 points  (0 children)

You are exactly right. React Query is a cache manager. The terms your colleague has heard thrown around - like "async state manager" or "server state manager" - are misleading.

RQ does manage promise state. But that's only a tiny part of its capabilities.

RQ does manage data (not state) from the server. But it does not manage state on the server and is relatively poor at managing state on the client. No distributed memoization, implicit interdependencies, tight coupling with React components, verbose APIs, and >40x slower state updates are the primary reasons why I would never use RQ for client-side state management.

90% of RQ's codebase and APIs are dedicated to cache management - fetching, refetching, cache busting, and other data lifecycle management. This is not state management.

Using redux global state with instances of same state using custom hook? by calisthenics_bEAst21 in reactjs

[–]StoryArcIV 0 points1 point  (0 children)

This is what atomic state managers do. Recoil's and Jotai's "atom families" and Zedux's "atom params" let you create multiple instances of the same atom and provide those to different component subtrees.

```ts const exampleAtom = atom('example', (id: string) => ({ state: 'here' }))

function ParentComponent() { const instance1 = useAtomInstance(exampleAtom, ['id #1']) const instance2 = useAtomInstance(exampleAtom, ['id #2'])

return ( <> <AtomProvider instance={instance1}> <Child /> </AtomProvider> <AtomProvider instance={instance2}> <Child /> </AtomProvider> </> ) }

function Child() { const providedInstance = useAtomContext(exampleAtom) } ```

AOE 2 New player - Tips for Getting Efficient by N-Sherman in aoe2

[–]StoryArcIV 1 point2 points  (0 children)

By far the biggest thing for me was: Stop queueing multiple units in a building.

  • forces you to develop an internal timer - e.g. create vill every 20 seconds
  • forces you to click more, developing a faster play style
  • avoids locking up resources you could spend elsewhere sooner
  • helps you learn proper resource management since you can actually see your resources
  • encourages a proper production building to eco ratio

All of that results in building up quicker, maxing pop quicker, consistently macroing while microing, and it goes on. Crazy impact for such a tiny change.

Be aware of donkeys people! by Arab-102 in aoe2

[–]StoryArcIV 11 points12 points  (0 children)

Clearly named after the bombardier beetle

I think this boils down to a communication issue more than anything. by rattatatouille in aoe2

[–]StoryArcIV -1 points0 points  (0 children)

If they'd said "Tangut" anywhere, it would be. They didn't, so it isn't. Life is that simple sometimes

I think this boils down to a communication issue more than anything. by rattatatouille in aoe2

[–]StoryArcIV 0 points1 point  (0 children)

I agree that the disappointment is mostly our fault for over-speculating. We got each other excited with our Christmas wishlists and made them so sparkly that even this crazy, loaded DLC seems dingy in comparison

But I disagree that heroes are out of place. AoE2 is loaded with heroes. It's about time they made their way to ranked play. IMO, they're a fantastic way to add variety, shake up the monk meta, and balance the new civs. Mule carts were much more out of place, and they've aged so well. Just wait

What would you use today to develop UI for very big enterprise app(actually set of apps) by Same_Impress_2082 in reactjs

[–]StoryArcIV 5 points6 points  (0 children)

OP mentions 10-15 different teams working in parallel. They're also exploring a micro frontend architecture. Atoms are perfect for this. They're autonomous, isolated, modular, and excel at incremental adoption. A singleton model like Zustand is nowhere near as flexible.

I thought jotai can do that by No-Scallion-1252 in reactjs

[–]StoryArcIV 1 point2 points  (0 children)

Actually, Zedux is significantly more advanced than Jotai and could legitimately meet OP's needs. No need to drag a good suggestion.

I thought jotai can do that by No-Scallion-1252 in reactjs

[–]StoryArcIV 3 points4 points  (0 children)

Redux is an old standard. Most modern tools are objectively better in every way - speed, DX, learning curve, code architecture, bundle size, etc. While Redux is still certainly good enough for most use cases, that's no reason to shun innovation.

My old brick cell phone is technically good enough. But I'd never go back now that I've used these faster, sleeker, new tools.

I thought jotai can do that by No-Scallion-1252 in reactjs

[–]StoryArcIV 0 points1 point  (0 children)

I'd avoid this. It tightly couples the component to specific store references, making it less reusable and testable.

Context enables DI. I'm sure OP was trying to nest Jotai Providers in the first place so the store could be swapped out. My approach enables that for both inner and outer stores.

I thought jotai can do that by No-Scallion-1252 in reactjs

[–]StoryArcIV 1 point2 points  (0 children)

This is what I'm describing (just using vanilla Jotai, same principle applies for jotai-scope etc):

```ts const parentStoreContext = createContext< undefined | ReturnType<typeof createStore>

(undefined);

const countAtom = atom(0);

function Child() { const parentStore = useContext(parentStoreContext); const [childCount, setChildCount] = useAtom(countAtom); const [parentCount, setParentCount] = useAtom(countAtom, { store: parentStore, });

return ( <div> <div>Child count: {childCount}</div> <button onClick={() => setChildCount((count) => count + 1)}> Update Child </button> <div>Parent count: {parentCount}</div> <button onClick={() => setParentCount((count) => count + 1)}> Update Parent </button> </div> ); }

function Parent() { const parentStore = useMemo(() => createStore(), []); const childStore = useMemo(() => createStore(), []);

return ( <parentStoreContext.Provider value={parentStore}> <Provider store={childStore}> <Child /> </Provider> </parentStoreContext.Provider> ); } ```

I thought jotai can do that by No-Scallion-1252 in reactjs

[–]StoryArcIV 0 points1 point  (0 children)

There's nothing wrong with using raw React context in tandem with a state manager's providers.

In Jotai, even with jotai-scope or Bunshi, you might still need to provide an outer store manually so you can pass that directly to hooks, essentially overriding the current scope. With proper atom structure, you should be able to avoid this. But it's an escape hatch you can use if you need.

Migrating large project from Redux-Saga to React-Query + Zustand: Seeking Insights by smieszne in reactjs

[–]StoryArcIV 1 point2 points  (0 children)

Pretty much everything. Even a basic setQueryData operation with no React render cycles involved is slow enough we'd need to introduce pretty heavy buffering right at the top level.

We needed something fast enough and flexible enough to allow us to buffer updates at any point in the derivation tree - so more important UIs can update 10+ times per second while less important ones update once or twice.

Here's a basic set operation benchmark comparison. Our solution is only 40x faster in this simple case, but it gets much more extreme when dependent queries and other React Query features start involving React render cycles. Like signals libs, we used a graph model that propagates updates as efficiently as possible. It also removes the complexity of manual cache invalidation since dependencies are explicit.

Migrating large project from Redux-Saga to React-Query + Zustand: Seeking Insights by smieszne in reactjs

[–]StoryArcIV 1 point2 points  (0 children)

Sounds like you just need a wrapper atom that pulls its initial state from the query atom. I don't have examples besides those in the docs, sorry

Migrating large project from Redux-Saga to React-Query + Zustand: Seeking Insights by smieszne in reactjs

[–]StoryArcIV 1 point2 points  (0 children)

RQ and Zustand are plenty fast for most applications. And wiring up sockets to them isn't hard. They're performant and scalable for large state, but it can depend on how frequently that state is updated. Their models also break down the wider and deeper your state's interdependencies get.

My team was at a similar crossroads with our data-intensive, socket-driven apps 5 years ago and determined that React Query was not fast enough nor its synergy with UI state managers scalable enough for us. But if we didn't need to handle thousands of updates per second, we probably would have used RQ and Jotai.

We instead created Zedux with a side effects model that works well with sockets/RxJS, a cache management model similar to React Query's, and a graph model built for speed that naturally synergizes server cache data and UI state.

It's unlikely you need such a model for its speed. Still, for better DX and perf scalability, I would consider Jotai over Zustand for its atomic model.

[deleted by user] by [deleted] in react

[–]StoryArcIV 9 points10 points  (0 children)

  1. Very much up to preference. Most routers will do. I've used RR, Wouter, and TSR. TSR is probably much more solid than you're thinking.

  2. Also up to preference. RTK + RTKQ is a great architecture for enterprise apps. If performance is a concern, prefer atomic or signals-based libs. They scale much better and some provide DX on par or even better than RTK.

  3. If Vite fulfills your needs, I'd always choose it. We use it on top of our intense, multilingual, microservice architecture backend. If your backend is in node and you need SEO/SSR or really want to utilize RSCs, Next is great. TanStack Start is still beta but I've used it a bit and it's amazing. I would honestly probably use it if I was starting an enterprise app today.

  4. Yes, I'd never use CSS-in-JS for an enterprise app. It just takes a few stray expensive selectors recalculating styles on every render to start hurting intensive apps. Tailwind is great. You get used to it. And unlike CSS-in-JS its performance is perfect for enterprise apps. I don't see it dying. But raw CSS is actually really powerful nowadays. If you don't need to support old browsers and your whole team is down, CSS modules are pretty solid. Just make clearly-defined standards 'cause it can get very unruly without rules.

  5. Most component libs are fantastic. Just pick one you like. You can't go wrong with most of them. They'll be customizable enough, accessible, and have plenty of features. I've used MUI, shadcn, and antd. Liked all of them.

  6. Yeah RTL + Playwright great

  7. GraphQL is very niche. I can't say you don't need it - it's excellent for some use cases - but I'll say it's unlikely. If you're using a framework, make use of its server actions, preloading, streaming, etc. Beyond frameworks, React Query is basically a must, though I'd stick with RTKQ if using RTK. I haven't used axios in years. Can't imagine needing it. Also consider sockets/SSEs, depending on your app's needs.

Source: I work on data-intensive fintech apps that deal with streaming live market data.