🪟 Windows TippsThe Gemini desktop app is now available for Windows(11.09.2026 um 17:06 Uhr)
🪟 Windows TippsIntegrated GPU is showing as Removable on Windows 11(14.09.2026 um 06:48 Uhr)
⚠️ Malware / Trojaner / VirenWindows 11 just dropped the tool ransomware abused, Microsoft says don’t restore WMIC(10.09.2026 um 20:11 Uhr)
⚠️ Malware / Trojaner / VirenVorsicht: Android-Malware verschlüsselt Ihre Handys und nimmt heimlich Fotos auf(13.09.2026 um 09:30 Uhr)
🪟 Windows TippsIkea bringt neuen Bluetooth-Lautsprecher auf den Markt: Badkruka(14.09.2026 um 08:00 Uhr)
🪟 Windows TippsLinuxWelt Extra 3/2026 am Kiosk: Linux Grundlagen erklärt(14.09.2026 um 09:45 Uhr)
🪟 Windows TippsThe Gemini desktop app is now available for Windows(11.09.2026 um 17:06 Uhr)
🪟 Windows TippsIntegrated GPU is showing as Removable on Windows 11(14.09.2026 um 06:48 Uhr)
⚠️ Malware / Trojaner / VirenWindows 11 just dropped the tool ransomware abused, Microsoft says don’t restore WMIC(10.09.2026 um 20:11 Uhr)
⚠️ Malware / Trojaner / VirenVorsicht: Android-Malware verschlüsselt Ihre Handys und nimmt heimlich Fotos auf(13.09.2026 um 09:30 Uhr)
🪟 Windows TippsIkea bringt neuen Bluetooth-Lautsprecher auf den Markt: Badkruka(14.09.2026 um 08:00 Uhr)
🪟 Windows TippsLinuxWelt Extra 3/2026 am Kiosk: Linux Grundlagen erklärt(14.09.2026 um 09:45 Uhr)

🔧 Programmierung 🕛 vor 2 Jahren 9 Min Lesezeit
0

Two-way Binding can be a One-way Street

↗ Quelle (dev.to)
🗣️ Stimme:
📑 Inhaltsübersicht

Ryan wrote a into the same category as Vue when it comes to its approach to data flow, so I wanted to set the record straight.



And maybe show a better path that other frameworks could adopt. 🤷






Recap: Problems with two-way binding



Ryan lays out the problem with two-way binding and it boils down to a handful of issues:






1. Unpredictable data flow




the creator of that state has no idea if and how the child will mutate it







3. Unpredictable performance




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







Solving these problems



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.



The solution: Convention 🤝 & Sugar 🍬.



But let's start from the beginning. Look at the Solid example Ryan showed under the heading (another great article by Ryan).







Adding Convention 🤝



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:




CODE
<let/name="world" />
<Input value=name valueChange(v) { name = v } />









CODE
// Input.marko
<input
value=input.value
onInput(e) { input.valueChange(e.target.value) }
>






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



Perhaps we make a specific NameInput and it's used like this:




CODE
<NameInput name=name nameChange(v) { name = v } />









Adding Sugar 🍬



Now that we have this convention where it's easy for us to see someAttribute and someAttributeChange correspond to each other, it's also easy for a compiler to see.



Marko introduces the := shorthand, which makes these two lines equivalent:




CODE
<Input value:=name />
<Input value=name valueChange(v) { name = v } />






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




NOTE: Sourcemaps even map the generated valueChange function to the := in the source template, so when you're debugging as you step into the call to valueChange from the child, you'll see where it was passed from the parent.







Upgrading the DOM



So this convention is great, but what about the simple case with <input>?



Marko adds several *Change attributes to native HTML elements. So instead of using the onInput event, you can use valueChange:




CODE
<input
value=input.value
valueChange=input.valueChange
>






And both of the following are equivalent to the above:




CODE
<input value:=input.value>
<input ...input>







NOTE: 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 (key, ref, on:, @, etc.)




This is great! For the common propagating case, we can use :=. 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 valueChange function without needing to refactor anywhere else in my app.






Additional benefit: Controllable components



You might have heard the terms . For example, <dialog> in React doesn't have open and defaultOpen. It also operates in a partially controlled state.






Marko's solution



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 do listen for changes, you now take full resposibility for the corresponding value.



To illustrate this, in Marko the following yields an <input> that ignores your keystrokes:




CODE
<input value="world" valueChange() {}>






We passed valueChange which causes the input to be controlled, but it's an empty function, so no state is ever updated. The <input> effectively ignores our keystrokes.






Extending to components



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!



Marko enables this by making its core state primitive, the <let> tag, controllable.



Let's take our uncontrolled counter component:




CODE
<let/count=0 />
<button onClick() { count += 1 }>
${count}
</button>






In Marko, we have an unnamed attribute that defaults to value, so the following are equivalent:




CODE
<let/count=0 />
<let/count value=0 />






In this usage <let> is uncontrolled: it maintains it own internal state that it provides to us.



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




CODE
<let/count value=0 valueChange(v) { alert(v) } />
<button onClick() { count += 1 }>
${count}
</button>






Okay, so how is this useful? We can pass an optional change handler from the parent:




CODE
<let/count value=input.value valueChange=input.valueChange />






Now, if the parent passes valueChange, it controls the internal count. If it doesn't, the <let> maintains the count.



And of course, we can still use the := shorthand, so here is our controllable counter:




CODE
<let/count:=input.count />
<button onClick() { count += 1 }>
${count}
</button>









Conclusion



So Marko introduces a zero-cost abstraction that looks like two way data binding, but is ackchyually one-way data flow:




CODE
<let/name="world" />
<Input value:=name />









CODE
// Input.marko
<input value:=input.value>






Is functionally equivalent to:




CODE
<let/name="world" />
<Input value=name valueChange(v) { name = v } />









CODE
// Input.marko
<input
value=input.value
onInput(e) { input.valueChange(e.target.value) }
>






And…




  1. Data flow is explict

  2. There no way to introduce implicit loops

  3. It's performant

  4. You can opt-out of the sugar at any level

  5. (bonus) The convention opens the door for controllable components



Win-win-win-win-win 🎉






Marko



Everything we discussed is available in Marko 6 which is currenly in pre-release, but getting more stable every day.



I hope you'll try it out!

Vollständiger Original-Bericht
Ausführliche Details, Code-Beispiele & Hersteller-Stellungnahme auf dev.to.
↗ Original-Artikel auf dev.to lesen
Wie bewertest du diesen Beitrag?
1 Klick Feedback
Teilen mit Netzwerk & Team:

Community-Analysen & Experten-Meinungen 0

Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
Community Pulse: Relevanz-Einschätzung
1 Klick Experten-Votum
🔴 Akute Relevanz 0%
🟡 In Evaluierung 0%
🟢 Keine Auswirkung 0%
Spannende Innovation 0%
Verwandte Story-Cluster & Quellen (Vektor-KI)
Port 8095 Engine
1 Quelle
The Gemini desktop app is now available for Windows
1 Quelle
Integrated GPU is showing as Removable on Windows 11
1 Quelle
Windows 11 just dropped the tool ransomware abused, Microsoft says don’t restore WMIC
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Two-way Binding can be a One-way Street

Thematisch verwandte Begriffe: Twoway, Binding, Oneway, Street · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...