Repository navigation
Replies: 5 comments 6 replies
|
This is a great discussion, appreciate the question. First, the I've built something with fine grained dependency tracking and updates in the past. I found that using a "vanilla" language (C#, C++, etc.) without some type of DSL on top became very problematic - all functions ended up needing to take State instead of T everywhere. it was hard to have ignorant/transparent code, everything kinda needed to know about the data model/dependency system. I think that Compose got away with this with their Composable (@composable) attribute and code rewriting, which is their DSL. With this, we started with the basis of wanting to use unmodified C# compiler, no code rewriter and exact semantics of the C# language as it exists today. I'm open to any of the options, I think it would be great to explore alternative designs, I am pretty fixed on keeping with C# semantics though, I want to avoid something like a fork of C# to address limitations. As for how far we explored alternatives - we did some initial exploration, but then relatively quickly settled on a working path and wanted to see how far it could go. Since this was an experiment, we optimized for "time to market" and getting something we can use to gather feedback. Once we started going down the React style path, it was easy to make progress and focus on getting a cohesive story... I think we have a pretty stable set of bits now, I think getting the AI to do a port to the two different models you propose would be really interesting to compare if these is developer/agent ergonomic wins or performance wins with the other two approaches. At the moment, the functional style and C# focus seems to be working, and resonating. A next step of getting conviction on the state model and C# shape would be a good next step. I'm curious, what would you use as the primary evaluation function to determine which of the 3 paths you'd go down? All three have market success and large stable developer bases, so it's not like one of the 3 has proven to be the "winner". |
|
Thank you for such a candid and insightful response. It is very helpful to understand the practical trade-offs behind the project’s early phase, particularly the choice to leverage the unmodified C# compiler and optimize for time-to-market. I would love to address your question regarding the "primary evaluation function" from the perspective of how reactivity models have evolved across web and mobile platforms, and how WinUI might leverage its late-mover advantage. 1. Decoupling "State Tracking" from "Template Construction"When evaluating these paradigms, it is beneficial to separate how reactive data is tracked from how the UI layout is described.
You correctly noted that Jetpack Compose achieves its model via a powerful but heavy DSL and code rewriter. However, we do not necessarily need that level of compiler complexity. If the vanilla C# language is expressive enough to construct a React-style declarative object tree, it is structurally capable of supporting a SolidJS-style evaluation network. The developer-facing syntax for writing templates remains largely identical; the core difference lies entirely in how reactive dependencies are wired underneath. If we maintain the current JSX-like complexity without adding heavy compiler forks, the template-writing experience will remain unmodified. 2. The Value of Front-End AnalogsThe reason I brought up SolidJS and Lit as equivalents to Compose and SwiftUI is that evaluating architectures across entirely different hardware/OS platforms often introduces extraneous variables. When looking at React, SwiftUI, and Compose in isolation, it is easy to categorize them broadly as "different flavors of MVU." However, by mapping them to their web-equivalent architectures, the underlying differences in state management and render boundaries become much more transparent and easier to compare objectively. 3. Industry Consensus and the Evolution Away from "Indiscriminate Re-runs"The front-end ecosystem has spent over a decade refining reactive models. Today, the consensus has moved significantly toward Signals and highly localized updates (either bypassing the Virtual DOM entirely or minimizing its footprint).
For the developer, whether a framework uses a Virtual DOM or not has a minor impact on daily template composition. However, whether a framework uses a Signal-based model versus a React-style Hook model radically changes developer ergonomics. Currently, React stands as one of the few major frameworks that still relies on the "indiscriminate function re-run" model. React’s immense success is largely rooted in its first-mover advantage and a highly mature ecosystem, rather than the perfection of its core reactive model. In fact, the React community has had to build numerous state-management abstractions precisely to alleviate the stale-closure issues and unnecessary re-renders inherent in its design. 4. The Evaluation Function: Embracing the Late-Mover AdvantageTo answer your question on the primary evaluation function: it should optimize for Developer Ergonomics (lowering mental tax) and predictable runtime behavior. While comparing React and Jetpack Compose can be complex due to platform differences, comparing React and SolidJS offers a direct architectural benchmark. In terms of developer experience, the Signal model naturally preserves closures and standard local variables because the setup block runs only once. It removes the need for developers to manually manage lifecycle patches like useMemo or useCallback. For developers who have worked with verbose XAML and manual data-binding for years, any declarative/MVU solution represents a major step forward, which naturally explains the positive early feedback Reactor is receiving. However, considering the evolutionary trajectory of both web and mobile architecture, WinUI has a unique opportunity to embrace a late-mover advantage. Rather than defaulting to the React paradigm because it provided the fastest path for an early experiment, the evaluation function should look toward the future. We can adopt a model that sidesteps the inherent closure complexities right as the rest of the industry is actively moving past them. I completely agree that having an AI agent port a cohesive slice of code into a Solid-style Signal model versus the current React Hook model would be an excellent next step. Comparing the developer ergonomics and predictability between these two approaches would be highly valuable for the project's evolution. Looking forward to seeing where Reactor goes next! |
|
Without having looked at it in too much detail, personally I think I would also prefer a DOM-less approach with lambdas. I think performance should be prioritized for a native framework from the beginning. Also I feel like it would be easier to migrate from the previous Although maybe not as nice to type - that's what AI will do for us going forward... |
|
Just posted a similar question, then I see this post. |
|
Chris Anderson (@codemonkeychris) Do you have new thoughts about this? The more time passes, the harder it gets to change stuff like this... |
Uh oh!
There was an error while loading. Please reload this page.
I’ve been closely following the development of Microsoft.UI.Reactor and am very intrigued by its design philosophy, especially with its recent expansion to support multiple languages. The documentation states:
While I deeply appreciate the ambition to bring elegant pure-code declarative patterns to WinUI, I'd love to spark a friendly architectural discussion regarding how Reactor defines its reactive boundaries.
When we strip away the surface syntax, modern declarative frameworks generally operate on two orthogonal axes: State Lifecycle (In-View vs. External/Independent) and View Update Granularity (Virtual Tree Diffing vs. Fine-grained/Smart Updates). If we categorize the industry's mainstream paradigms based on their actual runtime behavior, we can see that React, SwiftUI, and Jetpack Compose are fundamentally different animals:
1. State Co-located + Virtual Tree Diffing (React / Reactor)
2. State Externalized + Fine-grained Direct Updates (Jetpack Compose / SolidJS)
3. State Bound to Properties + Template/Identity Slot Updates (SwiftUI / Lit)
@Observable/@Statenodes) to an underlying AttributeGraph. When data changes, SwiftUI doesn't blindly re-run heavy rendering logic; instead, it uses the structural Identity of lightweight view structs to evaluate graph deltas and surgically push updates to the render server.My Question
Given this landscape, "co-located state" does not have to mean locking the state's lifecycle inside a transient rendering function loop. Frameworks like Compose, Solid, and Lit have proven that we can achieve beautiful, file-level co-location without inheriting React's closure baggage.
What led to the architectural decision to adopt the specific React-like hook model (forcing state inside the Render() loop via UseState), which inherently forces developers to manage the mental tax of stale closures and manual memoization in a native desktop/app environment?
Did the team consider alternative blueprints—such as externalizing the state to live in a long-lived, independent layer (similar to Compose/Solid) or utilizing property/identity-bound slot updates (similar to SwiftUI/Lit)—which would keep the UI purely declarative while providing a much more stable environment for heavy asynchronous logic?
I would love to hear the team's insights on these reactive design trade-offs. Thank you for your incredible work on pushing the WinUI ecosystem forward!
All reactions