<?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: Michael Rawlings</title>
    <description>The latest articles on DEV Community by Michael Rawlings (@mlrawlings).</description>
    <link>https://dev.to/mlrawlings</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%2F388184%2Fdb041cf7-af67-4e35-b89e-dea14707f1d9.jpeg</url>
      <title>DEV Community: Michael Rawlings</title>
      <link>https://dev.to/mlrawlings</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mlrawlings"/>
    <language>en</language>
    <item>
      <title>Two-way Binding can be a One-way Street</title>
      <dc:creator>Michael Rawlings</dc:creator>
      <pubDate>Thu, 01 Aug 2024 21:55:10 +0000</pubDate>
      <link>https://dev.to/mlrawlings/two-way-binding-can-be-a-one-way-street-1o3</link>
      <guid>https://dev.to/mlrawlings/two-way-binding-can-be-a-one-way-street-1o3</guid>
      <description>&lt;p&gt;Ryan wrote a &lt;a href="https://dev.to/this-is-learning/two-way-binding-is-a-two-way-street-2d3c"&gt;lovely article&lt;/a&gt; about two-way data binding… but I do take a bit of issue with &lt;a href="https://dev.to/this-is-learning/two-way-binding-is-a-two-way-street-2d3c#:~:text=But%20I%27ve%20also%20worked%20on%20Marko%20which%20does%20and%20resembles%20Vue%27s%20approach."&gt;Marko getting lumped&lt;/a&gt; into the same category as Vue when it comes to its approach to data flow, so I wanted to set the record straight. &lt;/p&gt;

&lt;p&gt;And maybe show a better path that other frameworks could adopt. 🤷&lt;/p&gt;

&lt;h2&gt;
  
  
  Recap: Problems with two-way binding
&lt;/h2&gt;

&lt;p&gt;Ryan lays out the problem with two-way binding and it boils down to a handful of issues:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Unpredictable data flow
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;the creator of that state has no idea if and how the child will mutate it &lt;br&gt;
—&lt;a href="https://dev.to/this-is-learning/two-way-binding-is-a-two-way-street-2d3c#:~:text=the%20creator%20of%20that%20state,will%20mutate%20it"&gt;Ryan Carniato&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;
  Example
  &lt;p&gt;This is a problem for libraries that don't have read-write segregation. Where passing a value implicitly grants access to modifying that value as well. &lt;/p&gt;

&lt;p&gt;Take the following Preact Signals example:&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;Component&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;count&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="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Child&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Is &lt;code&gt;count&lt;/code&gt; a signal or a number?&lt;/li&gt;
&lt;li&gt;If it is a signal, does &lt;code&gt;Child&lt;/code&gt; only read &lt;code&gt;count.value&lt;/code&gt; or does it also &lt;em&gt;write&lt;/em&gt; it?&lt;/li&gt;
&lt;li&gt;If the child &lt;em&gt;does&lt;/em&gt; write to &lt;code&gt;count.value&lt;/code&gt; and we're debugging, we see that write, but then jump into internals and it's not immediately clear where that write propagated to.&lt;/li&gt;
&lt;/ol&gt;



&lt;br&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Potential for infinite loops
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;it does not take much to create "invisible" loops where updates travel up and down the render tree&lt;br&gt;
—&lt;a href="https://dev.to/this-is-learning/two-way-binding-is-a-two-way-street-2d3c#:~:text=it%20does%20not%20take%20much,the%20render%20tree."&gt;Ryan Carniato&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  3. Unpredictable performance
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;A top-down rendering approach(like a VDOM, or dirty checker) might realize a change had happened part way down its update cycle and then have to start over again&lt;br&gt;
—&lt;a href="https://dev.to/this-is-learning/two-way-binding-is-a-two-way-street-2d3c#:~:text=A%20top%2Ddown%20rendering%20approach,still%20impacts%20traceability."&gt;Ryan Carniato&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  4. Limits refactoring
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;One can always opt out of two-way binding if they need to intercept the change event on the native element. But propagating that ability to split it up through component hierarchies is a new consideration. If not available you may be left breaking out of these loops on the way back down. &lt;br&gt;
—&lt;a href="https://dev.to/this-is-learning/two-way-binding-is-a-two-way-street-2d3c#:~:text=One%20can%20always%20opt%20out,breaking%20out%20of%20these%20loops%20on%20the%20way%20back%20down."&gt;Ryan Carniato&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Solving these problems
&lt;/h2&gt;

&lt;p&gt;These issues are something we on the Marko team have thought extensively about. And there's a solution where we get to have the terseness of two-way binding while preserving one-way data flow and avoiding the issues listed above. &lt;/p&gt;

&lt;p&gt;The solution: &lt;strong&gt;Convention&lt;/strong&gt; 🤝 &amp;amp; &lt;strong&gt;Sugar&lt;/strong&gt; 🍬.&lt;/p&gt;

&lt;p&gt;But let's start from the beginning. Look at the Solid example Ryan showed under the heading &lt;a href="https://dev.to/this-is-learning/two-way-binding-is-a-two-way-street-2d3c#:~:text=3.%20Read/Write%20Segregation%20Everywhere"&gt;"Read/Write Segregation Everywhere"&lt;/a&gt;:&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;function&lt;/span&gt; &lt;span class="nf"&gt;App&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createSignal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;world&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// How do you 2-way bind this? You don't...&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Input&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt; &lt;span class="nx"&gt;onUpdate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;setName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;
    &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&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;onInput&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onUpdate&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="sr"&gt;/&lt;/span&gt;&lt;span class="err"&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;Here we have a nice, explicit, one-way flow of data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;onInput → onUpdate → setName → &amp;lt;Input&amp;gt;.value → &amp;lt;input&amp;gt;.value
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What about Marko?
&lt;/h3&gt;

&lt;p&gt;The same components look very similar in Marko:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;let&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"world"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Input&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt; &lt;span class="na"&gt;onUpdate&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="na"&gt;v&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name = &lt;/span&gt;&lt;span class="s"&gt;v&lt;/span&gt; &lt;span class="err"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Input.marko&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;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;
  &lt;span class="na"&gt;onInput&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="na"&gt;e&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onUpdate&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="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: Despite using an assignment, Marko still has read-write segregation: you can't modify the &lt;code&gt;name&lt;/code&gt; passed to the child, you &lt;em&gt;have&lt;/em&gt; to provide a function that modifies it in the local scope. Marko retains &lt;a href="https://dev.to/this-is-learning/thinking-locally-with-signals-3b7h"&gt;Locality of Thinking&lt;/a&gt; (another great article by Ryan).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Adding Convention 🤝
&lt;/h3&gt;

&lt;p&gt;This is a common pattern, so let's add some convention around it. If an event soley exists to propagate some value, let's name it as such:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;let&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"world"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Input&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt; &lt;span class="na"&gt;valueChange&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="na"&gt;v&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name = &lt;/span&gt;&lt;span class="s"&gt;v&lt;/span&gt; &lt;span class="err"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Input.marko&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;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;
  &lt;span class="na"&gt;onInput&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="na"&gt;e&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;valueChange&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="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is exactly the same as the above, except &lt;code&gt;onChange&lt;/code&gt; is now &lt;code&gt;valueChange&lt;/code&gt; (to match the &lt;code&gt;value&lt;/code&gt; attribute). This convention is the recommended way to propagate data up the tree in Marko: add &lt;code&gt;Change&lt;/code&gt; to the attribute name when passing a change handler for another attribute.&lt;/p&gt;

&lt;p&gt;Perhaps we make a specific &lt;code&gt;NameInput&lt;/code&gt; and it's used like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;NameInput&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt; &lt;span class="na"&gt;nameChange&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="na"&gt;v&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name = &lt;/span&gt;&lt;span class="s"&gt;v&lt;/span&gt; &lt;span class="err"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Adding Sugar 🍬
&lt;/h3&gt;

&lt;p&gt;Now that we have this convention where it's easy for us to see &lt;code&gt;someAttribute&lt;/code&gt; and &lt;code&gt;someAttributeChange&lt;/code&gt; correspond to each other, it's also easy for a compiler to see. &lt;/p&gt;

&lt;p&gt;Marko introduces the &lt;code&gt;:=&lt;/code&gt; shorthand, which makes these two lines equivalent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Input&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="na"&gt;name&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="nc"&gt;Input&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt; &lt;span class="na"&gt;valueChange&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="na"&gt;v&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using &lt;code&gt;:=&lt;/code&gt; is a lot more terse, but it still explictly gives the child a function to update a value. &lt;strong&gt;It's just syntax sugar.&lt;/strong&gt; The child doesn't care whether the parent used this shorthand or not. We haven't lost locality of thinking.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: Sourcemaps even map the generated &lt;code&gt;valueChange&lt;/code&gt; function to the &lt;code&gt;:=&lt;/code&gt; in the source template, so when you're debugging as you step into the call to &lt;code&gt;valueChange&lt;/code&gt; from the child, you'll see where it was passed from the parent.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Upgrading the DOM
&lt;/h3&gt;

&lt;p&gt;So this convention is great, but what about the simple case with &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;Marko adds several &lt;code&gt;*Change&lt;/code&gt; attributes to native HTML elements. So instead of using the &lt;code&gt;onInput&lt;/code&gt; event, you can use &lt;code&gt;valueChange&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&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;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;
  &lt;span class="na"&gt;valueChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;valueChange&lt;/span&gt;
&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And both of the following are equivalent to the above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&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;value&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&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="err"&gt;...&lt;/span&gt;&lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: Adding these attributes was a difficult decison; the purist in us really didn't want to, but it's a big win for consistency and composability within Marko. And you're probably already used to some extra attributes from other frameworks you use (&lt;code&gt;key&lt;/code&gt;, &lt;code&gt;ref&lt;/code&gt;, &lt;code&gt;on:&lt;/code&gt;, &lt;code&gt;@&lt;/code&gt;, etc.)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is great! For the common propagating case, we can use &lt;code&gt;:=&lt;/code&gt;. It's clear that we're giving the child a way to request a change, and if we need to do more we can add our own &lt;code&gt;valueChange&lt;/code&gt; function without needing to refactor anywhere else in my app.&lt;/p&gt;

&lt;h3&gt;
  
  
  Additional benefit: Controllable components
&lt;/h3&gt;

&lt;p&gt;You might have heard the terms &lt;a href="https://react.dev/learn/sharing-state-between-components#controlled-and-uncontrolled-components" rel="noopener noreferrer"&gt;"Controlled" and "Uncontrolled"&lt;/a&gt; in regards to components before. Quick recap:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Controlled components receive their state from their parent. They may &lt;em&gt;request&lt;/em&gt; changes to that data (through events), but they don't actually &lt;em&gt;control&lt;/em&gt; it.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="err"&gt;()&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;countChange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="si"&gt;}&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;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&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;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Uncontrolled components own their own state and can update it directly.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="na"&gt;0&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;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="err"&gt;()&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="si"&gt;}&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;count&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;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Generally, native HTML elements are uncontrolled. You can set an initial &lt;code&gt;value&lt;/code&gt; for an &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt;, but once you start typing in it, the &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; maintains its own state.&lt;/p&gt;

&lt;p&gt;However, it's often useful to have a form element (or other native element) controlled by your application state. &lt;/p&gt;

&lt;h4&gt;
  
  
  Other frameworks
&lt;/h4&gt;

&lt;p&gt;React acknowledges this: it supports both &lt;code&gt;value&lt;/code&gt; (controlled) and &lt;code&gt;defaultValue&lt;/code&gt; (uncontrolled) on &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;So if you use &lt;code&gt;value&lt;/code&gt; with no listener, you essentially get a read-only input:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&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;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"world"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Most other frameworks operate in a partially controlled state where the &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; maintains its own internal state, but you can update &lt;code&gt;value&lt;/code&gt;. So there's no guarantee the two are in sync.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://codesandbox.io/s/react-controlled-input-forked-uleknq" rel="noopener noreferrer"&gt;But even React is inconsistent&lt;/a&gt;. For example, &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; in React doesn't have &lt;code&gt;open&lt;/code&gt; and &lt;code&gt;defaultOpen&lt;/code&gt;. It also operates in a partially controlled state.&lt;/p&gt;

&lt;h4&gt;
  
  
  Marko's solution
&lt;/h4&gt;

&lt;p&gt;In Marko we're using the change handler to signal the desire for control. If you don't listen for changes you get an uncontrolled component. If you &lt;em&gt;do&lt;/em&gt; listen for changes, you now take full resposibility for the corresponding value.&lt;/p&gt;

&lt;p&gt;To illustrate this, in Marko the following yields an &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; that ignores your keystrokes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&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;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"world"&lt;/span&gt; &lt;span class="na"&gt;valueChange&lt;/span&gt;&lt;span class="err"&gt;()&lt;/span&gt; &lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We passed valueChange which causes the input to be controlled, but it's an empty function, so no state is ever updated. The &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; effectively ignores our keystrokes.&lt;/p&gt;

&lt;h4&gt;
  
  
  Extending to components
&lt;/h4&gt;

&lt;p&gt;This ability to operate as either controlled or uncontrolled isn't only useful for native tags. We want to be able to write our own controllable components!&lt;/p&gt;

&lt;p&gt;Marko enables this by making its core state primitive, the &lt;code&gt;&amp;lt;let&amp;gt;&lt;/code&gt; tag, controllable.&lt;/p&gt;

&lt;p&gt;Let's take our uncontrolled counter component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="na"&gt;0&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;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="err"&gt;()&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="si"&gt;}&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;count&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;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Marko, we have an unnamed attribute that defaults to &lt;code&gt;value&lt;/code&gt;, so the following are equivalent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="na"&gt;0&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;let&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;count&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="na"&gt;0&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this usage &lt;code&gt;&amp;lt;let&amp;gt;&lt;/code&gt; is uncontrolled: it maintains it own internal state that it provides to us.&lt;/p&gt;

&lt;p&gt;But if we pass a &lt;code&gt;valueChange&lt;/code&gt; handler, it no longer maintains its own state and reflects the &lt;code&gt;value&lt;/code&gt; passed to it. For example this counter would &lt;code&gt;alert(1)&lt;/code&gt; every time it was clicked &lt;em&gt;without updating count&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;count&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="na"&gt;0&lt;/span&gt; &lt;span class="na"&gt;valueChange&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="na"&gt;v&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&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;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="err"&gt;()&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="si"&gt;}&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;count&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;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Okay, so how is this useful?&lt;/strong&gt; We can pass an optional change handler from the parent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;count&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt; &lt;span class="na"&gt;valueChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;valueChange&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, if the parent passes &lt;code&gt;valueChange&lt;/code&gt;, it controls the internal &lt;code&gt;count&lt;/code&gt;. If it doesn't, the &lt;code&gt;&amp;lt;let&amp;gt;&lt;/code&gt; maintains the &lt;code&gt;count&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;And of course, we can still use the &lt;code&gt;:=&lt;/code&gt; shorthand, so here is our &lt;strong&gt;controllable&lt;/strong&gt; counter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;count&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;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="err"&gt;()&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="si"&gt;}&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;count&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;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;So Marko introduces a zero-cost abstraction that &lt;em&gt;looks&lt;/em&gt; like two way data binding, but is &lt;em&gt;ackchyually&lt;/em&gt; one-way data flow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"world"&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="nc"&gt;Input&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Input.marko&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;value&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Is functionally equivalent to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"world"&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="nc"&gt;Input&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt; &lt;span class="na"&gt;valueChange&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="na"&gt;v&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Input.marko&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;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;
  &lt;span class="na"&gt;onInput&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="na"&gt;e&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;valueChange&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="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;And…&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Data flow is explict&lt;/li&gt;
&lt;li&gt;There no way to introduce implicit loops&lt;/li&gt;
&lt;li&gt;It's performant&lt;/li&gt;
&lt;li&gt;You can opt-out of the sugar at any level&lt;/li&gt;
&lt;li&gt;(bonus) The convention opens the door for controllable components
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Win-win-win-win-win 🎉&lt;/p&gt;

&lt;h2&gt;
  
  
  Marko
&lt;/h2&gt;

&lt;p&gt;Everything we discussed is available in Marko 6 which is currenly in pre-release, but getting more stable every day. &lt;/p&gt;

&lt;p&gt;I hope you'll try it out!&lt;/p&gt;

</description>
      <category>marko</category>
      <category>solidjs</category>
      <category>react</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Your SSR is slow &amp; your devtools are lying to you</title>
      <dc:creator>Michael Rawlings</dc:creator>
      <pubDate>Tue, 22 Mar 2022 22:12:29 +0000</pubDate>
      <link>https://dev.to/mlrawlings/your-ssr-is-slow-your-devtools-are-lying-to-you-3056</link>
      <guid>https://dev.to/mlrawlings/your-ssr-is-slow-your-devtools-are-lying-to-you-3056</guid>
      <description>&lt;p&gt;As developers we want our sites to be fast, and it takes many small wins to add up to a performant site.&lt;/p&gt;

&lt;p&gt;I want to talk specifically about two performance factors, and how your devtools might mislead you to believe they're not worth pursuing, leaving your users with a slower experience. Those two factors are &lt;strong&gt;rendering&lt;/strong&gt; and &lt;strong&gt;streaming&lt;/strong&gt;.  &lt;/p&gt;




&lt;h2&gt;
  
  
  Rendering
&lt;/h2&gt;

&lt;p&gt;Let's start with rendering.  The reality is, many of us are building websites using tools that are primarily focused on client-side updates.  It's typically easiest for these tools to replicate the browser environment on the server to generate the initial HTML, so that's what many of them do - whether it's a full-blown headless browser, jsdom, or a virtual dom.  &lt;/p&gt;

&lt;p&gt;On the lighter end of this spectrum (vdom), the performance is typically considered "good enough", where that's often tens of milliseconds, compared to a purpose-built, string-based HTML renderer that may be more like 1ms.  &lt;/p&gt;

&lt;p&gt;These same frameworks also perform a process called "hydration" which typically involves serializing a &lt;em&gt;lot&lt;/em&gt; of data down to the browser to make the page interactive.  This serialization process consumes valuable cpu time and further delays the response.&lt;/p&gt;

&lt;p&gt;Okay, but does it really matter if your page takes an extra 50ms to load? Maybe not.  But what about concurrent requests?  See, rendering is a cpu-bound (blocking) task: if rendering takes 50ms and 10 requests come in at roughly the same time (to the same render process), the 10th is left waiting for 450ms before it can start doing its work and respond.  Looking at the response time of a single request isn't giving you the full picture.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F1958812%2F159560970-a5ca4834-0f78-4b54-8293-03a0ee3f110a.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F1958812%2F159560970-a5ca4834-0f78-4b54-8293-03a0ee3f110a.gif" alt="blocking render"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Streaming
&lt;/h2&gt;

&lt;p&gt;Next up, streaming.  Specifically, early flushing of HTML before we have all the data necessary to render the entire page.  If you don't stream, your page is going to be as slow as your slowest upstream dependency.  Streaming decouples your Time to First Byte (TTFB) from your data sources and enables the browser to start rendering and fetching known resources earlier.  Depending on the speed of your upstream data sources, this can have a significant impact.&lt;/p&gt;

&lt;p&gt;It doesn't only affect your TTFB but hastens First Contentful Paint (FCP) allowing the earlier display of available content and loading indicators. And depending on how broken up the page is, it also allows hydration to occur earlier and piecewise. Ultimately streaming can have a positive impact on Time to Interactive (TTI) as well.&lt;/p&gt;

&lt;p&gt;Even if your data sources are pretty fast, at worst your content ultimately reaches the end-user at the same time. But when your API, database, or network hits an outlier on latency, streaming has you covered.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F1958812%2F159561135-d2571876-5743-49ae-bc6b-7c8c636e35a1.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F1958812%2F159561135-d2571876-5743-49ae-bc6b-7c8c636e35a1.gif" alt="streaming render"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Emulating Slowdown in Devtools
&lt;/h2&gt;

&lt;p&gt;If you're testing performance, you typically want to understand the worst-case scenario.  Everything's going to look fast for the person on the Mac Studio M1 Ultra with 10 Gigabit Ethernet.  No, you want to understand how your site feels for the person on an older android device over a spotty cellular network. And that last part, the slow network, is where we run into trouble.&lt;/p&gt;

&lt;p&gt;The way devtools emulates slow networks &lt;em&gt;hides the impact of server-originated delays&lt;/em&gt;.  If we dig into what the presets, such as "Slow 3G" and "Fast 3G" are doing we can see why:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F1958812%2F159048789-18eceb31-c0ad-45bc-85ba-b6113fa3c286.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F1958812%2F159048789-18eceb31-c0ad-45bc-85ba-b6113fa3c286.png" alt="network throttling"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You'll see here there is a "latency" setting, which ensures the request takes &lt;em&gt;at least&lt;/em&gt; that long, but...&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;DevTools doesn’t add an extra delay if the specified latency is already met. &lt;br&gt;
    - &lt;a href="https://www.debugbear.com/blog/network-throttling-methods" rel="noopener noreferrer"&gt;Matt Zeunert, DebugBear&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What? So on "Slow 3G" where the latency is 2s, that means whether the server responds instantly or takes the full 2 seconds to respond, devtools shows the same timing for those requests? &lt;strong&gt;Yup&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You can try it yourself. Take a look at the timing for these two pages without devtools network throttling and then with "Slow 3G":&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://restless-cake-d61e.mlrawlings.workers.dev/?ttfb=0" rel="noopener noreferrer"&gt;https://restless-cake-d61e.mlrawlings.workers.dev/?ttfb=0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://restless-cake-d61e.mlrawlings.workers.dev/?ttfb=2000" rel="noopener noreferrer"&gt;https://restless-cake-d61e.mlrawlings.workers.dev/?ttfb=2000&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;p&gt;You'll notice I didn't include any hard numbers in here.  Things like server architecture will make these factors more or less relevant. Do your own testing on real devices &amp;amp; networks. Even more so, look at what your &lt;em&gt;actual users&lt;/em&gt; are experiencing—especially at the &lt;a href="https://timkadlec.com/remembers/2018-06-07-prioritizing-the-long-tail-of-performance/" rel="noopener noreferrer"&gt;long tail&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It doesn't help that we're often locked into a certain class of SSR performance before we ever get to the stage of testing these things.  If you've built your app using one of the afore-mentioned client-focused tools, you may have to reconsider that decision or hope you can find enough wins elsewhere.&lt;/p&gt;

&lt;p&gt;While there may be other factors impacting your site's performance, making your server respond faster is only going to improve things. And don't let your devtools fool you: if something's slower over a fast network, it's going to be slower over a slow network as well.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>performance</category>
    </item>
  </channel>
</rss>
