<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Robert Sanders</title>
    <description>The latest articles on DEV Community by Robert Sanders (@robert_sanders_04918a4344).</description>
    <link>https://dev.to/robert_sanders_04918a4344</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3753575%2F70506f5c-5420-4ab2-8239-93de0346c1a0.jpg</url>
      <title>DEV Community: Robert Sanders</title>
      <link>https://dev.to/robert_sanders_04918a4344</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/robert_sanders_04918a4344"/>
    <language>en</language>
    <item>
      <title>Side Effects: Angular Signals vs rs-x</title>
      <dc:creator>Robert Sanders</dc:creator>
      <pubDate>Wed, 18 Mar 2026 14:24:16 +0000</pubDate>
      <link>https://dev.to/robert_sanders_04918a4344/side-effects-angular-signals-vs-rs-x-3gld</link>
      <guid>https://dev.to/robert_sanders_04918a4344/side-effects-angular-signals-vs-rs-x-3gld</guid>
      <description>&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;You want to react to state changes and run some code as a side effect.&lt;/p&gt;

&lt;p&gt;Every reactive library solves this. But &lt;strong&gt;how&lt;/strong&gt; they solve it changes a lot about your code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Angular Signals — &lt;code&gt;effect()&lt;/code&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;effect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;price changed:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;price&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ Simple to read&lt;br&gt;&lt;br&gt;
⚠️ Re-runs when &lt;strong&gt;any&lt;/strong&gt; signal read inside changes&lt;br&gt;&lt;br&gt;
⚠️ Requires Angular's runtime and compiler&lt;br&gt;&lt;br&gt;
⚠️ Lifecycle managed separately from your data  &lt;/p&gt;

&lt;h2&gt;
  
  
  rs-x — The Sequence Expression
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;expr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;rsx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;(trackPrice(price), trackQty(quantity), price * quantity)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;(a, b, c)&lt;/code&gt; is standard JavaScript.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The comma operator evaluates left to right and returns the last value.&lt;br&gt;&lt;br&gt;
rs-x parses it exactly as a JS engine would — no new syntax, no compiler.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Fine-Grained by Default
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;rsx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;(trackPrice(price), trackQty(quantity), price * quantity)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;//       ↑ tracks price       ↑ tracks quantity        ↑ return value&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Change&lt;/th&gt;
&lt;th&gt;Re-runs&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;model.price = 150&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;trackPrice&lt;/code&gt; only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;model.quantity = 5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;trackQty&lt;/code&gt; only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Both change&lt;/td&gt;
&lt;td&gt;Both re-run&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Each segment tracks its own dependencies — &lt;strong&gt;independently&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conditional Side Effects
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;rsx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;(count &amp;gt; limit &amp;amp;&amp;amp; onOverflow(count), count)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; short-circuits — &lt;strong&gt;standard JavaScript behaviour&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
&lt;code&gt;onOverflow&lt;/code&gt; is only called when the guard is truthy.&lt;br&gt;&lt;br&gt;
&lt;code&gt;count&lt;/code&gt; is still tracked as the return value regardless.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lifecycle — Zero Ceremony
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;expr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;rsx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;(notify(status), status)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Later...&lt;/span&gt;
&lt;span class="nx"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispose&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// ← every side effect cleaned up automatically&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Angular Signals&lt;/th&gt;
&lt;th&gt;rs-x&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Register&lt;/td&gt;
&lt;td&gt;&lt;code&gt;effect(() =&amp;gt; {...})&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;part of the expression string&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cleanup&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;DestroyRef&lt;/code&gt; / manual&lt;/td&gt;
&lt;td&gt;&lt;code&gt;expr.dispose()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scope&lt;/td&gt;
&lt;td&gt;Angular component&lt;/td&gt;
&lt;td&gt;the expression itself&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Syntax&lt;/td&gt;
&lt;td&gt;Framework API&lt;/td&gt;
&lt;td&gt;standard JavaScript&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Cross-Property Tracking
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// trackView watches userId&lt;/span&gt;
&lt;span class="c1"&gt;// formatName watches profile.name&lt;/span&gt;
&lt;span class="c1"&gt;// Each is independent&lt;/span&gt;

&lt;span class="nf"&gt;rsx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;(trackView(userId), formatName(profile.name))&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Change &lt;code&gt;userId&lt;/code&gt; → &lt;code&gt;trackView&lt;/code&gt; re-runs, &lt;code&gt;formatName&lt;/code&gt; does not&lt;br&gt;&lt;br&gt;
Change &lt;code&gt;profile.name&lt;/code&gt; → &lt;code&gt;formatName&lt;/code&gt; re-runs, &lt;code&gt;trackView&lt;/code&gt; does not&lt;/p&gt;

&lt;h2&gt;
  
  
  The Philosophy
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;No new API surface. No compiler. Just JavaScript expressions, tracked reactively.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Side effects are co-located with the data they read.&lt;br&gt;&lt;br&gt;
Their lifecycle is owned by the expression, not your application code.&lt;br&gt;&lt;br&gt;
When the expression is disposed — the side effects go with it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn More
&lt;/h2&gt;

&lt;p&gt;📖 &lt;strong&gt;Side effects deep dive&lt;/strong&gt; → &lt;a href="https://rsxjs.com/docs/core-concepts/side-effects" rel="noopener noreferrer"&gt;https://rsxjs.com/docs/core-concepts/side-effects&lt;/a&gt;&lt;br&gt;&lt;br&gt;
▶️ &lt;strong&gt;Live playground&lt;/strong&gt; → &lt;a href="https://rsxjs.com/playground" rel="noopener noreferrer"&gt;https://rsxjs.com/playground&lt;/a&gt;&lt;br&gt;&lt;br&gt;
⭐ &lt;strong&gt;rs-x on GitHub&lt;/strong&gt; → github.com/robert-sanders-software-ontwikkeling/rs-x&lt;/p&gt;

</description>
      <category>angular</category>
      <category>frontend</category>
      <category>typescript</category>
      <category>signals</category>
    </item>
    <item>
      <title>Reactive Data Without the Async Headaches</title>
      <dc:creator>Robert Sanders</dc:creator>
      <pubDate>Wed, 04 Mar 2026 23:06:40 +0000</pubDate>
      <link>https://dev.to/robert_sanders_04918a4344/reactive-data-without-the-async-headaches-5fbd</link>
      <guid>https://dev.to/robert_sanders_04918a4344/reactive-data-without-the-async-headaches-5fbd</guid>
      <description>&lt;p&gt;Modern frontend development often turns into a battle with async code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  async/await&lt;/li&gt;
&lt;li&gt;  subscriptions&lt;/li&gt;
&lt;li&gt;  dependency arrays&lt;/li&gt;
&lt;li&gt;  memoization&lt;/li&gt;
&lt;li&gt;  race conditions&lt;/li&gt;
&lt;li&gt;  unnecessary recomputations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What if you could simply &lt;strong&gt;describe your data&lt;/strong&gt; and let the system&lt;br&gt;
handle the rest?&lt;/p&gt;

&lt;p&gt;That's exactly the idea behind &lt;strong&gt;rs-x&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To demonstrate it, I built a small demo that tracks the &lt;strong&gt;real-time&lt;br&gt;
position of the International Space Station (ISS)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;👉 Live demo: &lt;a href="https://robert-sanders-software-ontwikkeling.github.io/rs-x/demo/?data=const+%24+%3D+api.rxjs%3B%0Aconst+rsx+%3D+api.rsx%3B%0A%0A%2F%2F+Poll+ISS+API%0Aconst+issRaw%24+%3D+%24.interval%282000%29.pipe%28%0A++%24.startWith%280%29%2C%0A++%24.switchMap%28%28%29+%3D%3E%0A++++%24.from%28%0A++++++fetch%28%27https%3A%2F%2Fapi.wheretheiss.at%2Fv1%2Fsatellites%2F25544%27%29.then%28%28r%29+%3D%3E%0A++++++++r.json%28%29%0A++++++%29%0A++++%29%0A++%29%2C%0A++%24.map%28%28data%29+%3D%3E+%28%7B%0A++++ts%3A+Number%28data.timestamp+%3F%3F+0%29%2C%0A++++lat%3A+Number%28data.latitude+%3F%3F+NaN%29%2C%0A++++lon%3A+Number%28data.longitude+%3F%3F+NaN%29%2C%0A++++altKm%3A+Number%28data.altitude+%3F%3F+NaN%29%2C%0A++++velKph%3A+Number%28data.velocity+%3F%3F+NaN%29%2C%0A++%7D%29%29%2C%0A++%24.shareReplay%28%7B+bufferSize%3A+1%2C+refCount%3A+true+%7D%29%0A%29%3B%0A%0A%2F%2F+Only+emit+when+coordinates+change+meaningfully%0Aconst+geoInputs%24+%3D+issRaw%24.pipe%28%0A++%24.map%28%28x%29+%3D%3E+%28%7B%0A++++latQ%3A+Math.round%28x.lat+*+100%29+%2F+100%2C%0A++++lonQ%3A+Math.round%28x.lon+*+100%29+%2F+100%2C%0A++%7D%29%29%2C%0A++%24.distinctUntilChanged%28%0A++++%28a%2C+b%29+%3D%3E+a.latQ+%3D%3D%3D+b.latQ+%26%26+a.lonQ+%3D%3D%3D+b.lonQ%0A++%29%0A%29%3B%0A%0Aconst+model+%3D+%7B%0A++iss%3A+issRaw%24%2C%0A++geo%3A+geoInputs%24%2C%0A%0A++heartbeat%3A+0%2C%0A++expensiveRuns%3A+0%2C%0A%0A++%2F%2F+Expensive+computation%0A++expensiveGeoScore%28lat%2C+lon%29+%7B%0A++++model.expensiveRuns%2B%2B%3B%0A%0A++++let+acc+%3D+0%3B%0A++++for+%28let+i+%3D+0%3B+i+%3C+3_000_000%3B+i%2B%2B%29+%7B%0A++++++acc+%2B%3D+Math.sin%28i%29%3B%0A++++%7D%0A%0A++++const+score+%3D%0A++++++Math.round%28%28Math.abs%28lat%29+*+1.7+%2B+Math.abs%28lon%29+*+0.9%29+*+100%29+%2F+100%3B%0A%0A++++console.log%28%27%5BEXPENSIVE%5D+run%3A%27%2C+model.expensiveRuns%2C+%27score%3A%27%2C+score%29%3B%0A++++return+score%3B%0A++%7D%2C%0A%7D%3B%0A%0A%2F%2F+Unrelated+model+update%0AsetInterval%28%28%29+%3D%3E+%7B%0A++model.heartbeat%2B%2B%3B%0A++console.log%28%27%5BMODEL%5D+heartbeat%3A%27%2C+model.heartbeat%29%3B%0A%7D%2C+5000%29%3B%0A%0A%2F%2F+Declarative+expression%0Areturn+rsx%28%60%0A%28%7B%0A++heartbeat%2C%0A%0A++issTs%3A+iss.ts%2C%0A++altKm%3A+iss.altKm%2C%0A++velKph%3A+iss.velKph%2C%0A%0A++lat%3A+iss.lat%2C%0A++lon%3A+iss.lon%2C%0A%0A++geoScore%3A+expensiveGeoScore%28geo.latQ%2C+geo.lonQ%29%2C%0A++expensiveRuns%0A%7D%29%0A%60%29%28model%29%3B" rel="noopener noreferrer"&gt;https://robert-sanders-software-ontwikkeling.github.io/rs-x/demo&lt;/a&gt;&lt;/p&gt;


&lt;h1&gt;
  
  
  The Idea
&lt;/h1&gt;

&lt;p&gt;The demo polls a public ISS API and exposes the data as part of a&lt;br&gt;
reactive model. ⚠️ Note  &amp;gt; If many people open the demo simultaneously, the public ISS API may return &lt;strong&gt;Too Many Requests&lt;/strong&gt; errors due to rate limits.&lt;/p&gt;

&lt;p&gt;It returns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  timestamp&lt;/li&gt;
&lt;li&gt;  latitude&lt;/li&gt;
&lt;li&gt;  longitude&lt;/li&gt;
&lt;li&gt;  altitude&lt;/li&gt;
&lt;li&gt;  velocity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At the same time the model contains an &lt;strong&gt;expensive computation&lt;/strong&gt; to&lt;br&gt;
demonstrate how rs-x handles dependencies.&lt;/p&gt;

&lt;p&gt;The key idea:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Only recompute what actually depends on changed data.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h1&gt;
  
  
  The Demo
&lt;/h1&gt;

&lt;p&gt;The API is polled every few seconds using RxJS.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rxjs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rsx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rsx&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;issRaw$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;switchMap&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.wheretheiss.at/v1/satellites/25544&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;latitude&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;NaN&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;lon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;longitude&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;NaN&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;altKm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;altitude&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;NaN&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;velKph&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;velocity&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;NaN&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;})),&lt;/span&gt;
  &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shareReplay&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;bufferSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;refCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Derived Reactive Data
&lt;/h1&gt;

&lt;p&gt;Next we derive a stream that only emits when the ISS position&lt;br&gt;
meaningfully changes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;geoInputs$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;issRaw$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;latQ&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lat&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;lonQ&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lon&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;})),&lt;/span&gt;
  &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;distinctUntilChanged&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;latQ&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;latQ&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lonQ&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lonQ&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This prevents unnecessary recomputation.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Reactive Model
&lt;/h1&gt;

&lt;p&gt;Now we define the model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;iss&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;issRaw$&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;geo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;geoInputs$&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

  &lt;span class="na"&gt;heartbeat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;expensiveRuns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

  &lt;span class="nf"&gt;expensiveGeoScore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lon&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expensiveRuns&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;acc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="nx"&gt;_000_000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;acc&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
      &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;1.7&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lon&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The function intentionally simulates a &lt;strong&gt;CPU-heavy computation&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  A Completely Declarative Expression
&lt;/h1&gt;

&lt;p&gt;Finally we describe the data we want.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;rsx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
({
  heartbeat,

  issTs: iss.ts,
  altKm: iss.altKm,
  velKph: iss.velKph,

  lat: iss.lat,
  lon: iss.lon,

  geoScore: expensiveGeoScore(geo.latQ, geo.lonQ),

  expensiveRuns
})
`&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it.&lt;/p&gt;




&lt;h1&gt;
  
  
  What Happens When Data Changes?
&lt;/h1&gt;

&lt;p&gt;Two independent updates happen in the demo.&lt;/p&gt;

&lt;h3&gt;
  
  
  ISS position updates
&lt;/h3&gt;

&lt;p&gt;When the ISS position changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  lat&lt;/li&gt;
&lt;li&gt;  lon&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The expensive computation runs again.&lt;/p&gt;

&lt;h3&gt;
  
  
  Heartbeat updates
&lt;/h3&gt;

&lt;p&gt;A separate part of the model changes every few seconds.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;heartbeat&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This &lt;strong&gt;does not trigger the expensive computation&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Why?&lt;/p&gt;

&lt;p&gt;Because the expression only depends on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  geo.latQ&lt;/li&gt;
&lt;li&gt;  geo.lonQ&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;rs-x tracks these dependencies automatically.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why This Matters
&lt;/h1&gt;

&lt;p&gt;In many systems you need to manually manage recomputation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  useMemo&lt;/li&gt;
&lt;li&gt;  useEffect&lt;/li&gt;
&lt;li&gt;  dependency arrays&lt;/li&gt;
&lt;li&gt;  caching&lt;/li&gt;
&lt;li&gt;  manual optimization&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With rs-x the rule is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Describe your data and its relationships.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The runtime builds a &lt;strong&gt;reactive dependency graph&lt;/strong&gt; and recomputes only&lt;br&gt;
what is required.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Mental Model
&lt;/h1&gt;

&lt;p&gt;Think of it like a spreadsheet.&lt;/p&gt;

&lt;p&gt;If cell &lt;strong&gt;A1&lt;/strong&gt; changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  only cells that depend on &lt;strong&gt;A1&lt;/strong&gt; update&lt;/li&gt;
&lt;li&gt;  unrelated cells remain untouched&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;rs-x brings this model to &lt;strong&gt;JavaScript expressions&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Result
&lt;/h1&gt;

&lt;p&gt;The demo creates a &lt;strong&gt;reactive computation graph&lt;/strong&gt; that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  handles async streams&lt;/li&gt;
&lt;li&gt;  tracks dependencies automatically&lt;/li&gt;
&lt;li&gt;  avoids unnecessary recomputation&lt;/li&gt;
&lt;li&gt;  keeps code declarative and simple&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No async orchestration.&lt;/p&gt;

&lt;p&gt;No manual dependency tracking.&lt;/p&gt;

&lt;p&gt;Just describe the data.&lt;/p&gt;




&lt;h1&gt;
  
  
  Support the Project
&lt;/h1&gt;

&lt;p&gt;If you like &lt;strong&gt;rs-x&lt;/strong&gt;, consider supporting the project:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/sponsors/robert-sanders-software-ontwikkeling" rel="noopener noreferrer"&gt;https://github.com/sponsors/robert-sanders-software-ontwikkeling&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It helps me continue building &lt;strong&gt;open source tools for developers&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  Tags
&lt;/h1&gt;

&lt;h1&gt;
  
  
  javascript #reactiveprogramming #rxjs #opensource #webdevelopment
&lt;/h1&gt;

</description>
      <category>frontend</category>
      <category>javascript</category>
      <category>showdev</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Software Engineering After AI</title>
      <dc:creator>Robert Sanders</dc:creator>
      <pubDate>Thu, 26 Feb 2026 14:19:18 +0000</pubDate>
      <link>https://dev.to/robert_sanders_04918a4344/software-engineering-after-ai-3544</link>
      <guid>https://dev.to/robert_sanders_04918a4344/software-engineering-after-ai-3544</guid>
      <description>&lt;p&gt;About a year ago, I started experimenting with using AI to develop software.&lt;/p&gt;

&lt;p&gt;At first, I wasn’t very impressed. I had quite high expectations, partly influenced by the media. But then I began to realize that you have to guide the AI by asking the right questions. Don’t expect the AI to generate readable, maintainable code if you simply say, “Can you implement feature X for me?” It will just do that (sometimes incorrectly) and generate one big blob of code.&lt;/p&gt;

&lt;p&gt;Writing code with AI is similar to agile development: you work in small steps. In the diagram below, you can see the workflow I used. I added a brain icon to indicate where human interaction is especially important. You need to be able to ask the right questions to push the AI in the right direction. Without proper guidance, the AI quickly get lost.&lt;/p&gt;

&lt;p&gt;Sometimes the AI is simply not able to solve your problem. In those cases, you have to implement it yourself.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr1xgt553lhkrjm5seqeg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr1xgt553lhkrjm5seqeg.png" alt="Vibe coding" width="800" height="948"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I was quite impressed with the results. I built a user interface with React (I normally use Angular) and implemented a fairly complex application in just two weeks. Along the way, I also learned a lot about React. Normally, this would have taken me months. I was able to keep the code manageable by constantly refactoring, and I used AI here as well to speed up the process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson learned:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vibe coding without deep domain knowledge is an illusion. Without solid software engineering fundamentals, you are not guiding the AI — you are wandering with it. The quality of the outcome is directly proportional to the quality of the questions you ask.&lt;/li&gt;
&lt;li&gt;Mastery of a specific language or framework is becoming less critical. I don’t need to be a React expert to build a solid React application. What matters far more is architectural thinking, system design, and a strong understanding of web fundamentals and UX principles etc. These are the skills that allow you to evaluate, refine, and direct the AI toward the right solution.&lt;/li&gt;
&lt;li&gt;The role of the software engineer is evolving. We are becoming conductors of intelligent systems. Instead of writing every line of code, we orchestrate, refine, and shape the output. This shift demands experience, judgment, and the right mindset. The future belongs to engineers who understand systems deeply and can collaborate effectively with AI.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can see the result here: &lt;a href="https://robert-sanders-software-ontwikkeling.github.io/rs-x/" rel="noopener noreferrer"&gt;https://robert-sanders-software-ontwikkeling.github.io/rs-x/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For more information see rs-x opensource project: &lt;a href="https://github.com/robert-sanders-software-ontwikkeling/rs-x" rel="noopener noreferrer"&gt;https://github.com/robert-sanders-software-ontwikkeling/rs-x&lt;/a&gt;&lt;/p&gt;

</description>
      <category>vibecoding</category>
      <category>react</category>
      <category>ai</category>
      <category>programming</category>
    </item>
    <item>
      <title>Managing the React useState hell</title>
      <dc:creator>Robert Sanders</dc:creator>
      <pubDate>Sun, 15 Feb 2026 12:44:13 +0000</pubDate>
      <link>https://dev.to/robert_sanders_04918a4344/managing-the-react-usestate-hell-1l56</link>
      <guid>https://dev.to/robert_sanders_04918a4344/managing-the-react-usestate-hell-1l56</guid>
      <description>&lt;p&gt;React depends heavily on immutable data to detect changes.&lt;br&gt;
You define state explicitly, and React re-renders when references change.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ChangEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ChangeEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLInputElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt;  &lt;span class="nx"&gt;Sum&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setA&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setB&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onAChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ChangEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt;  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;setA&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onBChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ChangEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt;  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;setB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;  &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onAChange&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;  &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;  &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onBChange&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;  &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; + &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; = &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;sum&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, we only have two pieces of state, and it is still manageable.&lt;/p&gt;

&lt;p&gt;But when the state in your component starts to grow, it becomes difficult to manage. You begin losing track of which parts of state exist and how they interact.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Structured Approach
&lt;/h2&gt;

&lt;p&gt;To improve this, you can reorganize your code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Define an interface for your state&lt;/li&gt;
&lt;li&gt;Define a class that builds a new state based on changes&lt;/li&gt;
&lt;li&gt;Use a single useState call&lt;/li&gt;
&lt;li&gt;Use a state builder to construct new state instances&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This separates state-building logic from the component and gives you a clear overview of your state structure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt;  &lt;span class="nx"&gt;IAppSTate&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt;  &lt;span class="kd"&gt;class&lt;/span&gt;  &lt;span class="nc"&gt;AppStateBuilder&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt;  &lt;span class="nx"&gt;_state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IAppSTate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt;  &lt;span class="kd"&gt;get&lt;/span&gt;  &lt;span class="nf"&gt;state&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;IAppSTate&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_state&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt;  &lt;span class="nf"&gt;setA&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;a&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt;  &lt;span class="nf"&gt;setB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;b&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ChangEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ChangeEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLInputElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt;  &lt;span class="nx"&gt;Sum&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IAppSTate&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onAChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ChangEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt;  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;prevState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;  
            &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AppStateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prevState&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;setA&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onBChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ChangEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt;  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;prevState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 
            &lt;span class="k"&gt;new&lt;/span&gt;  &lt;span class="nc"&gt;AppStateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prevState&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;setB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;  &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onAChange&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;  &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onBChange&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; + &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; = &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;sum&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This reduces complexity by separating state construction logic from UI logic.&lt;br&gt;
However, this approach still has a major drawback.&lt;/p&gt;

&lt;p&gt;If your state contains nested objects, arrays, or maps, updating state becomes increasingly complex. You must constantly ensure that you create new references for every modified branch of the object tree.&lt;/p&gt;

&lt;p&gt;This leads to more spreading, more copying, and more room for mistakes.&lt;/p&gt;
&lt;h2&gt;
  
  
  A Third Approach: Reactive Models using (RS-X)
&lt;/h2&gt;

&lt;p&gt;Instead of manually rebuilding immutable state trees, you can use a reactive model approach.&lt;/p&gt;

&lt;p&gt;With RS-X:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;You create a stable reference to your model (using React.useRef)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You make the model reactive&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You update fields directly&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The UI updates automatically&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useRsxModel&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt;  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@rs-x/react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt;  &lt;span class="nx"&gt;IAppSTate&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ChangEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ChangeEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLInputElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt;  &lt;span class="nx"&gt;Sum&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt;  &lt;span class="nx"&gt;modelRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IAppSTate&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt;  &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;modelRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;useRsxModel&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IAppSTate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;IAppSTate&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onAChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ChangEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onBChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ChangEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;  &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onAChange&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;  &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onBChange&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; + &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; = &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;sum&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;References:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/robert-sanders-software-ontwikkeling/rs-x" rel="noopener noreferrer"&gt;RS-X github&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/robert_sanders_04918a4344/rs-x-now-works-with-react-4a56"&gt;RS-X with React&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackblitz.com/~/github.com/robert-sanders-software-ontwikkeling/rs-x-react-demo" rel="noopener noreferrer"&gt;RS-X React demo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>react</category>
      <category>state</category>
      <category>ts</category>
      <category>reactive</category>
    </item>
    <item>
      <title>Make Reactivity easy</title>
      <dc:creator>Robert Sanders</dc:creator>
      <pubDate>Sun, 15 Feb 2026 09:01:00 +0000</pubDate>
      <link>https://dev.to/robert_sanders_04918a4344/make-reactivity-easy-g3i</link>
      <guid>https://dev.to/robert_sanders_04918a4344/make-reactivity-easy-g3i</guid>
      <description>&lt;p&gt;Many UI frameworks make reactivity &lt;em&gt;feel unnatural&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;React relies heavily on &lt;strong&gt;immutability&lt;/strong&gt; so it can detect changes efficiently using reference checks.&lt;br&gt;&lt;br&gt;
That works, but it also means you often end up writing lots of copying/spreading code just to update state.&lt;/p&gt;

&lt;p&gt;Angular is moving toward a more reactive model with &lt;strong&gt;Signals&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
Signals are powerful, but they still introduce a new API and a different way of thinking:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Wrap values&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;set()&lt;/code&gt; / &lt;code&gt;update()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Add extra wiring (&lt;code&gt;effect()&lt;/code&gt; / &lt;code&gt;useEffect()&lt;/code&gt;) especially around async flows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But most developers don’t naturally think in “signals”.&lt;/p&gt;

&lt;p&gt;We think in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;data models&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;operations&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;plain TypeScript / JavaScript&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;and simply assigning values when something changes&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A More Natural Approach
&lt;/h2&gt;

&lt;p&gt;What if reactivity was not something you had to &lt;em&gt;learn&lt;/em&gt;…&lt;/p&gt;

&lt;p&gt;…but something that felt like a natural extension of JavaScript?&lt;/p&gt;

&lt;p&gt;That is the goal of &lt;strong&gt;RS-X&lt;/strong&gt;:&lt;br&gt;&lt;br&gt;
Make reactivity feel like part of TypeScript itself — similar to how &lt;strong&gt;LINQ&lt;/strong&gt; made SQL-like queries feel native in C#.&lt;/p&gt;

&lt;p&gt;You shouldn’t need a special API for async vs sync data either.&lt;br&gt;&lt;br&gt;
Just write your model, mix promises and values, and let the framework handle the rest.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;c&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;BehaviorSubject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;expression&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rsx&lt;/span&gt;&lt;span class="s2"&gt;`a + b + c`&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;changed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;//Will trigger change even&lt;/span&gt;
&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;70&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check out the &lt;a href="https://github.com/robert-sanders-software-ontwikkeling/rs-x" rel="noopener noreferrer"&gt;RS-X project on github&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>angular</category>
      <category>typescript</category>
      <category>reactive</category>
    </item>
    <item>
      <title>RS-X: simplifying reactive programming in React</title>
      <dc:creator>Robert Sanders</dc:creator>
      <pubDate>Tue, 10 Feb 2026 10:24:30 +0000</pubDate>
      <link>https://dev.to/robert_sanders_04918a4344/rs-x-simplifying-reactive-programming-in-react-2cp4</link>
      <guid>https://dev.to/robert_sanders_04918a4344/rs-x-simplifying-reactive-programming-in-react-2cp4</guid>
      <description>&lt;p&gt;RS-X is a reactive framework that enables you to think in a more declarative way about your data and operations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You define your data model&lt;/li&gt;
&lt;li&gt;You define your operations via expressions&lt;/li&gt;
&lt;li&gt;You bind the expressions to your data model&lt;/li&gt;
&lt;li&gt;You can now just manipulate the data, and the expressions will update automatically &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With RS-X, you don’t need to wire everything through &lt;code&gt;useState&lt;/code&gt;, &lt;code&gt;useEffect&lt;/code&gt;, or derived state.&lt;br&gt;&lt;br&gt;
Your &lt;strong&gt;data model itself becomes reactive&lt;/strong&gt;, and React simply renders the result.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changes with RS-X in React
&lt;/h2&gt;

&lt;p&gt;Instead of thinking in terms of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;syncing state&lt;/li&gt;
&lt;li&gt;effects&lt;/li&gt;
&lt;li&gt;memoized selectors&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You work directly with your &lt;strong&gt;data model&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When the model changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;dependent expressions update automatically&lt;/li&gt;
&lt;li&gt;React re-renders only where needed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No extra glue code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this is different
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;No need to mirror your data into React state&lt;/li&gt;
&lt;li&gt;No &lt;code&gt;useEffect&lt;/code&gt; chains to keep things in sync&lt;/li&gt;
&lt;li&gt;No derived state bugs&lt;/li&gt;
&lt;li&gt;You update the model → the UI follows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;RS-X tracks dependencies at the &lt;strong&gt;expression level&lt;/strong&gt;, not the component level.&lt;/p&gt;

&lt;h2&gt;
  
  
  Async is first-class
&lt;/h2&gt;

&lt;p&gt;RS-X also handles asynchronous data naturally:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Promises in your model are resolved reactively&lt;/li&gt;
&lt;li&gt;Expressions update when async values arrive&lt;/li&gt;
&lt;li&gt;No loading-state orchestration required&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How it fits into React
&lt;/h2&gt;

&lt;p&gt;RS-X doesn’t replace React — it &lt;strong&gt;simplifies what React has to manage&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;React stays focused on rendering.&lt;br&gt;&lt;br&gt;
RS-X handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;reactivity&lt;/li&gt;
&lt;li&gt;dependency tracking&lt;/li&gt;
&lt;li&gt;expression evaluation&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why I built this
&lt;/h2&gt;

&lt;p&gt;I wanted:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;reactive data without a global store&lt;/li&gt;
&lt;li&gt;fewer hooks and less boilerplate&lt;/li&gt;
&lt;li&gt;a model-driven way of thinking&lt;/li&gt;
&lt;li&gt;predictable updates without over-rendering&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;React support makes that approach practical in real applications.&lt;br&gt;&lt;br&gt;
You can see it &lt;strong&gt;in action on StackBlitz&lt;/strong&gt;: &lt;a href="https://stackblitz.com/~/github.com/robert-sanders-software-ontwikkeling/rs-x-react-demo" rel="noopener noreferrer"&gt;React demo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feedback is very welcome.&lt;/p&gt;

&lt;p&gt;— Robert&lt;/p&gt;

&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/robert_sanders_04918a4344/showing-the-power-of-rs-x-with-a-scary-credit-risk-formula-nlp"&gt;Showing the Power of RS-X with a “Scary” Credit-Risk Formula&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/robert_sanders_04918a4344/rs-x-reactive-state-and-expressions-for-javascript-and-typescript-3a76"&gt;RS-X: Reactive State and Expressions for JavaScript and TypeScript&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/robert_sanders_04918a4344/rs-x-now-works-with-react-4a56"&gt;Using RS-X with React&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackblitz.com/~/github.com/robert-sanders-software-ontwikkeling/rs-x-angular-demo" rel="noopener noreferrer"&gt;Angular demo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/robert-sanders-software-ontwikkeling/rs-x" rel="noopener noreferrer"&gt;RS-X GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;NPM packages:

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/@rs-x/core" rel="noopener noreferrer"&gt;@rs-x/core&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/@rs-x/state-manager" rel="noopener noreferrer"&gt;@rs-x/state-manager&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/@rs-x/expression-parser" rel="noopener noreferrer"&gt;@rs-x/expression-parser&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/@rs-x/angular" rel="noopener noreferrer"&gt;@rs-x/angular&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>react</category>
      <category>typescript</category>
      <category>javascript</category>
      <category>reactive</category>
    </item>
    <item>
      <title>Using RS-X with React 🎉</title>
      <dc:creator>Robert Sanders</dc:creator>
      <pubDate>Tue, 10 Feb 2026 09:26:27 +0000</pubDate>
      <link>https://dev.to/robert_sanders_04918a4344/rs-x-now-works-with-react-4a56</link>
      <guid>https://dev.to/robert_sanders_04918a4344/rs-x-now-works-with-react-4a56</guid>
      <description>&lt;p&gt;With RS-X, you don’t need to wire everything through &lt;code&gt;useState&lt;/code&gt;, &lt;code&gt;useEffect&lt;/code&gt;, or derived state.&lt;br&gt;&lt;br&gt;
Your &lt;strong&gt;data model itself becomes reactive&lt;/strong&gt;, and React simply renders the result.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changes with RS-X in React
&lt;/h2&gt;

&lt;p&gt;Instead of thinking in terms of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;syncing state&lt;/li&gt;
&lt;li&gt;effects&lt;/li&gt;
&lt;li&gt;memoized selectors&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You work directly with your &lt;strong&gt;data model&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When the model changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;dependent expressions update automatically&lt;/li&gt;
&lt;li&gt;React re-renders only where needed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No extra glue code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this is different
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;No need to mirror your data into React state&lt;/li&gt;
&lt;li&gt;No &lt;code&gt;useEffect&lt;/code&gt; chains to keep things in sync&lt;/li&gt;
&lt;li&gt;No derived state bugs&lt;/li&gt;
&lt;li&gt;You update the model → the UI follows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;RS-X tracks dependencies at the &lt;strong&gt;expression level&lt;/strong&gt;, not the component level.&lt;/p&gt;

&lt;h2&gt;
  
  
  Async is first-class
&lt;/h2&gt;

&lt;p&gt;RS-X also handles asynchronous data naturally:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Promises in your model are resolved reactively&lt;/li&gt;
&lt;li&gt;Expressions update when async values arrive&lt;/li&gt;
&lt;li&gt;No loading-state orchestration required&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How it fits into React
&lt;/h2&gt;

&lt;p&gt;RS-X doesn’t replace React — it &lt;strong&gt;simplifies what React has to manage&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;React stays focused on rendering.&lt;br&gt;&lt;br&gt;
RS-X handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;reactivity&lt;/li&gt;
&lt;li&gt;dependency tracking&lt;/li&gt;
&lt;li&gt;expression evaluation&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why I built this
&lt;/h2&gt;

&lt;p&gt;I wanted:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;reactive data without a global store&lt;/li&gt;
&lt;li&gt;fewer hooks and less boilerplate&lt;/li&gt;
&lt;li&gt;a model-driven way of thinking&lt;/li&gt;
&lt;li&gt;predictable updates without over-rendering&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;React support makes that approach practical in real applications.&lt;br&gt;&lt;br&gt;
You can see it &lt;strong&gt;in action on StackBlitz&lt;/strong&gt;: &lt;a href="https://stackblitz.com/~/github.com/robert-sanders-software-ontwikkeling/rs-x-react-demo" rel="noopener noreferrer"&gt;React demo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feedback is very welcome.&lt;/p&gt;

&lt;p&gt;— Robert&lt;/p&gt;

&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/robert_sanders_04918a4344/showing-the-power-of-rs-x-with-a-scary-credit-risk-formula-nlp"&gt;Showing the Power of RS-X with a “Scary” Credit-Risk Formula&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/robert_sanders_04918a4344/rs-x-reactive-state-and-expressions-for-javascript-and-typescript-3a76"&gt;RS-X: Reactive State and Expressions for JavaScript and TypeScript&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackblitz.com/~/github.com/robert-sanders-software-ontwikkeling/rs-x-angular-demo" rel="noopener noreferrer"&gt;Angular demo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/robert-sanders-software-ontwikkeling/rs-x" rel="noopener noreferrer"&gt;RS-X GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;NPM packages:

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/@rs-x/core" rel="noopener noreferrer"&gt;@rs-x/core&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/@rs-x/state-manager" rel="noopener noreferrer"&gt;@rs-x/state-manager&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/@rs-x/expression-parser" rel="noopener noreferrer"&gt;@rs-x/expression-parser&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/@rs-x/angular" rel="noopener noreferrer"&gt;@rs-x/angular&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>react</category>
      <category>angular</category>
      <category>typescript</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Showing the Power of RS-X with a “Scary” Credit-Risk Formula</title>
      <dc:creator>Robert Sanders</dc:creator>
      <pubDate>Thu, 05 Feb 2026 09:12:00 +0000</pubDate>
      <link>https://dev.to/robert_sanders_04918a4344/showing-the-power-of-rs-x-with-a-scary-credit-risk-formula-nlp</link>
      <guid>https://dev.to/robert_sanders_04918a4344/showing-the-power-of-rs-x-with-a-scary-credit-risk-formula-nlp</guid>
      <description>&lt;p&gt;When I explain RS-X, I like to use examples that look way more complicated than necessary.&lt;/p&gt;

&lt;p&gt;This helps us to test the power of RS-X. For this post, I want to use a credit-risk formula. With this formula, I can demonstrate the following features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mix synchronous and asynchronous data effortlessly.&lt;/li&gt;
&lt;li&gt;Modular expressions: support for splitting up a monolithic expression into sub-expressions.&lt;/li&gt;
&lt;li&gt;Automatic updating of the expression value when bound data changes. You can use your data model as the source of truth. Changing your data model will automatically update the value of the expression.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The formula (monolithic version)
&lt;/h2&gt;

&lt;p&gt;Here’s a “scary” credit-risk formula in JavaScript with model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//Model&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ICustomer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;income&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;employmentYears&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Credit info&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ICredit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;score&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;outstandingDebt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Market parameters&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;IMarket&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;baseInterestRate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Risk parameters&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;IRisk&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;volatilityIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="c1"&gt;// e.g., market volatility&lt;/span&gt;
    &lt;span class="nl"&gt;recessionProbability&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// e.g., probability of economic downturn&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Risk calculation parameters&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;IRiskCalcParameters&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;market&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IMarket&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;risk&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IRisk&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Make it asynchronous to simulate API calls for fetching data.&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;IRiskModel&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BehaviorSubject&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ICustomer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;credit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BehaviorSubject&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ICredit&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;riskParameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BehaviorSubject&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IRiskCalcParameters&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;//Nonolithic scary formula&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;credit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mf"&gt;0.4&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;credit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;outstandingDebt&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;income&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.6&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;employmentYears&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.03&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mf"&gt;0.15&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
     &lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;35&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
     &lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;55&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mf"&gt;0.00&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
     &lt;span class="mf"&gt;0.08&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;riskParameters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;risk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;volatilityIndex&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
     &lt;span class="nx"&gt;riskParameters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;risk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;recessionProbability&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;riskParameters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;market&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;baseInterestRate&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.75&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;HIGH&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;credit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mf"&gt;0.4&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;credit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;outstandingDebt&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;income&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.6&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;employmentYears&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.03&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mf"&gt;0.15&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
     &lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;35&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
     &lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;55&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mf"&gt;0.00&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
     &lt;span class="mf"&gt;0.08&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;riskParameters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;risk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;volatilityIndex&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
     &lt;span class="nx"&gt;riskParameters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;risk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;recessionProbability&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;riskParameters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;market&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;baseInterestRate&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.45&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MEDIUM&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;LOW&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This looks scary, or not 😄. So let's split it up into sub-expressions to make it more readable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Treat your data as living inputs
&lt;/h2&gt;

&lt;p&gt;The data model is simple:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;customer info
&lt;/li&gt;
&lt;li&gt;credit info
&lt;/li&gt;
&lt;li&gt;market/risk parameters
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These values might change independently or arrive asynchronously. RS-X doesn’t care. You just plug them into expressions, and updates flow automatically.  &lt;/p&gt;

&lt;p&gt;No listeners. No subscriptions. No &lt;code&gt;recalculateRiskScore()&lt;/code&gt; calls.&lt;/p&gt;




&lt;h2&gt;
  
  
  From monolith to modular
&lt;/h2&gt;

&lt;p&gt;Big formulas are messy and inefficient if calculated as a single block. RS-X lets you naturally split formulas into sub-expressions for efficiency and readability. Each sub-expression only recalculates when its inputs change.&lt;/p&gt;

&lt;h3&gt;
  
  
  Base personal risk
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;basePersonalRisk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;expressionFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`
    (credit.score &amp;lt; 600 ? 0.4 : 0.1) +
    (credit.outstandingDebt / customer.income) * 0.6 -
    (customer.employmentYears * 0.03)
`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;IExpression&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt; Calculates baseline risk from credit score, debt/income ratio, and employment history.&lt;/p&gt;

&lt;h3&gt;
  
  
  Age-based risk adjustment
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ageBasedRiskAdjustment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;expressionFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`
    customer.age &amp;lt; 25 ? 0.15 :
    customer.age &amp;lt; 35 ? 0.05 :
    customer.age &amp;lt; 55 ? 0.00 :
    0.08
`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;IExpression&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt; Modifies risk slightly based on age. Keeping it separate makes it readable and reusable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Market risk (async-friendly)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;marketRisk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;expressionFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`
    (riskParameters.risk.volatilityIndex * 0.5) +
    (riskParameters.risk.recessionProbability * 0.5)
`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;IExpression&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt; Market data arrives asynchronously, but RS-X updates automatically when it changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Interest rate impact
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;interestRateImpact&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;expressionFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`
    riskParameters.market.baseInterestRate * 2
`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;IExpression&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt; Updates automatically if base interest rate changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Composing the risk score
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;riskScore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;expressionFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;basePersonalRisk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;ageBasedRiskAdjustment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;marketRisk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;interestRateImpact&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="s2"&gt;`
    basePersonalRisk + ageBasedRiskAdjustment + marketRisk + interestRateImpact
`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt; Combines sub-expressions into a total score. Only recalculates when inputs change.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;riskClassification&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;expressionFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;riskScore&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;riskScore&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;IExpression&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;thresholds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;highRisk&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;mediumRisk&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.45&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="s2"&gt;`
    riskScore &amp;gt;= thresholds.highRisk
        ? 'HIGH'
        : riskScore &amp;gt;= thresholds.mediumRisk
            ? 'MEDIUM'
            : 'LOW'
`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt; Converts numeric score to HIGH/MEDIUM/LOW categories. Thresholds are reactive data — updates propagate automatically.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why this matters
&lt;/h2&gt;

&lt;p&gt;This pattern isn’t just for credit risk. You can use it for:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;pricing rules
&lt;/li&gt;
&lt;li&gt;validation logic
&lt;/li&gt;
&lt;li&gt;feature flags
&lt;/li&gt;
&lt;li&gt;scoring models
&lt;/li&gt;
&lt;li&gt;UI state
&lt;/li&gt;
&lt;li&gt;basically anything you can imagine
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final thought
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;RS-X supports  modular expressions that enables you to split up complex monolithic expression in reusable sub expressions&lt;/li&gt;
&lt;li&gt;async and sync data mix effortlessly
&lt;/li&gt;
&lt;li&gt;updates propagate automatically and efficiently&lt;/li&gt;
&lt;li&gt;You can just use your data model as source of true&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With RS-X, you can think in a more declarative way about your data and operations:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You define your data model
&lt;/li&gt;
&lt;li&gt;You define your operations via expressions
&lt;/li&gt;
&lt;li&gt;You bind the expressions to your data model
&lt;/li&gt;
&lt;li&gt;You can now just manipulate the data, and the expressions will update automatically&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can see this in action with the demos on StackBlitz: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://stackblitz.com/~/github.com/robert-sanders-software-ontwikkeling/rs-x-angular-demo" rel="noopener noreferrer"&gt;Angular demo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackblitz.com/~/github.com/robert-sanders-software-ontwikkeling/rs-x-react-demo" rel="noopener noreferrer"&gt;React demo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;References:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/robert-sanders-software-ontwikkeling/rs-x" rel="noopener noreferrer"&gt;RS-X GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;NPM packages:

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/@rs-x/core" rel="noopener noreferrer"&gt;@rs-x/core&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/@rs-x/state-manager" rel="noopener noreferrer"&gt;@rs-x/state-manager&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/@rs-x/expression-parser" rel="noopener noreferrer"&gt;@rs-x/expression-parser&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/@rs-x/angular" rel="noopener noreferrer"&gt;@rs-x/angular&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>reactiveprogramming</category>
      <category>statemanagement</category>
      <category>angular</category>
      <category>react</category>
    </item>
    <item>
      <title>RS-X: Reactive State and Expressions for JavaScript and TypeScript</title>
      <dc:creator>Robert Sanders</dc:creator>
      <pubDate>Wed, 04 Feb 2026 19:07:40 +0000</pubDate>
      <link>https://dev.to/robert_sanders_04918a4344/rs-x-reactive-state-and-expressions-for-javascript-and-typescript-3a76</link>
      <guid>https://dev.to/robert_sanders_04918a4344/rs-x-reactive-state-and-expressions-for-javascript-and-typescript-3a76</guid>
      <description>&lt;p&gt;Managing state and computations in JavaScript applications can be complex. Keeping data, dependent logic, and computations in sync often requires boilerplate and manual updates.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RS-X&lt;/strong&gt; is a library for managing &lt;strong&gt;reactive state and expressions&lt;/strong&gt; in JavaScript and TypeScript. It can make the parts of a plain object reactive that an expression depends on, so any expression automatically updates when its dependent data changes. While it can be used to drive UI updates, RS-X is &lt;strong&gt;framework-agnostic&lt;/strong&gt; and can be applied equally to business logic, server-side computations, or other reactive workflows.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reactive Expressions:&lt;/strong&gt; Expressions can reference synchronous or asynchronous values and automatically update when the underlying reactive data changes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reactive Models:&lt;/strong&gt; RS-X only makes reactive the parts of a model that an expression depends on. Each expression can reference its own model subset, and updates propagate to all dependent expressions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Framework-Agnostic:&lt;/strong&gt; RS-X works in any JavaScript or TypeScript environment. UI frameworks are optional consumers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Optional UI Integration:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Angular integration via rsx-pipe is available.&lt;/li&gt;
&lt;li&gt;React integration is &lt;strong&gt;planned&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Planned RS-X UI Framework:&lt;/strong&gt; RS-X will serve as the core engine for data binding. All data-binding expressions are parsed with the RS-X expression parser. The UI framework will consume models and expressions to perform local UI updates.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Automatic Change Detection:&lt;/strong&gt; Dependent expressions update automatically. Developers can also subscribe to the &lt;code&gt;changed&lt;/code&gt; event of an expression for programmatic reactions.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Compile-Time Safety (Planned):&lt;/strong&gt; Future TypeScript plugins will integrate RS-X expressions directly into TypeScript, similar to LINQ in C#, ensuring invalid expressions are caught before runtime and providing IntelliSense/autocomplete for reactive models.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  Impact and Use Cases
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Allows developers to &lt;strong&gt;reactively track changes&lt;/strong&gt; in models and expressions. Dependent expressions update automatically, while programmatic subscribers can still listen to the &lt;code&gt;changed&lt;/code&gt; event when needed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Reduces boilerplate in wiring expressions to models. RS-X will make the part of the model it needs reactive.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Improves maintainability by keeping the reactive model independent from the code that consumes it. Expressions and subscribers react automatically without needing to manage state manually.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Supports reactive workflows independent of any UI framework, letting models and expressions serve as the &lt;strong&gt;single source of truth&lt;/strong&gt; for computations.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;Check out the RS-X repository for examples, documentation, and an Angular integration demo on StackBlitz:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/robert-sanders-software-ontwikkeling/rs-x" rel="noopener noreferrer"&gt;RS-X GitHub repository&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackblitz.com/~/github.com/robert-sanders-software-ontwikkeling/rs-x-angular-demo" rel="noopener noreferrer"&gt;RS-X Angular demo on StackBlitz&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/robert_sanders_04918a4344/showing-the-power-of-rs-x-with-a-scary-credit-risk-formula-nlp"&gt;Complex expression example&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  NPM Packages
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/@rs-x/core" rel="noopener noreferrer"&gt;@rs-x/core&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/@rs-x/state-manager" rel="noopener noreferrer"&gt;@rs-x/state-manager&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/@rs-x/expression-parser" rel="noopener noreferrer"&gt;@rs-x/expression-parser&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/@rs-x/angular" rel="noopener noreferrer"&gt;@rs-x/angular&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>typescript</category>
      <category>reactiveprogramming</category>
      <category>react</category>
      <category>angular</category>
    </item>
  </channel>
</rss>
