Upgrade to Pro — share decks privately, control downloads, hide ads and more …

What the heck do “declarative” and “reactive” a...

What the heck do “declarative” and “reactive” actually mean?

You’ve built apps with frontend frameworks before? - Then you surely came across the terms "declarative" and “reactive”. Every modern UI framework or library wants to be it, but what do these terms actually mean? And what’s the difference between “declarative” and “imperative” code?

It took me a long while to grasp these concepts - but when I finally did, I saw frontend frameworks in a totally new light! And I hope you will feel the same after this talk.

We’ll delve into a lot of code examples to deeply understand the terms - with examples using vanilla JavaScript, React and Svelte. But the learnings will be universal. After this talk you’ll be able to confidently join discussions about the "declarative" and "reactive" nature of any new package or library.

Avatar for André Kovac

André Kovac

June 14, 2022
Tweet

More Decks by André Kovac

Other Decks in Programming

Transcript

  1. 3 “Pick up the toys, then empty the bin, then

    remove dust, then … and fi nally vacuum the fl oor.” IMPERATIVE Here’s a picture of what your room looks like when it’s clean. In 30 minutes this room will be clean. DECLARATIVE
  2. 5

  3. 7 AGENDA 1 BROWSER + APP STATE HOW DO THEY

    INTERACT? 2 IMPROVE WEB DEVELOPMENT WITH REACTIVENESS 4 HOW IS IT DONE? REACT VS. SVELTE 3 DESCRIBE USER INTERFACE DECLARATIVELY
  4. LET’S BUILD AN INTERACTIVE USER INTERFACE 9 <h1 id="number"></h1> <button

    onclick="onClick()"> Click to re-render </button> // initialize state let count = 0; // render default value document.getElementById("number").innerHTML = 0; const onClick = () => { // 1. update state count = count + 1; // 2. re-render user interface (side-effect) document.getElementById("number").innerHTML = count; }; HTML JAVASCRIPT
  5. 12

  6. 14 1 BROWSER READS <HTML /> AND CREATES DOCUMENT OBJECT

    MODEL (DOM) HTML FILE DOM JAVASCRIPT
  7. 15 1 BROWSER READS <HTML /> AND CREATES DOCUMENT OBJECT

    MODEL (DOM) 2 JAVASCRIPT ENGINE USES DOM TO RENDER THE USER INTERFACE
  8. 16 3 OUR JAVASCRIPT CODE CAN UPDATE THE DOM VIA

    THE WEB API (E.G. THE DOCUMENT OBJECT). 1 BROWSER READS <HTML /> AND CREATES DOCUMENT OBJECT MODEL (DOM) 2 JAVASCRIPT ENGINE USES DOM TO RENDER THE USER INTERFACE
  9. 17 3 OUR JAVASCRIPT CODE CAN UPDATE THE DOM VIA

    THE WEB API (E.G. THE DOCUMENT OBJECT). 1 BROWSER READS <HTML /> AND CREATES DOCUMENT OBJECT MODEL (DOM) 2 JAVASCRIPT ENGINE USES DOM TO RENDER THE USER INTERFACE APP STATE HAS TO CONSTANTLY SYNCHRONISE WITH THE UI
  10. SYNC OF APP STATE WITH USER INTERFACE 18 // initialize

    state let count = 0; // render default value document.getElementById("number").innerHTML = 7; const onClick = () => { // 1. update state count = count + 1; // 2. re-render user interface (side-effect) document.getElementById("number").innerHTML = count; }; UI JAVASCRIPT WHY IS THIS NOT OPTIMAL?
  11. COUNT 21 // declare and assign value let count =

    0; // re-assign value count = count + 1; // log value console.log({ count });
  12. NON-REACTIVE CODE VS. REACTIVE CODE 22 // declare and assign

    value let count = 0; // create copy let countCopy = count; // re-assign value count = count + 1; // log value console.log({ count, countCopy }); 🤔 MAKES SENSE COPY KEEPS FORMER VALUE OF "COUNT".
  13. NON-REACTIVE CODE VS. REACTIVE CODE 23 // declare and assign

    value let count = 0; // create copy let countCopy = count; // re-assign value count = count + 1; // 😕 have to sync count & countCopy countCopy = count; // log value console.log({ count, countCopy }); 💘 SYNC NECESSARY
  14. MAKE VARIABLE REACTIVE (NOT VALID JAVASCRIPT) 24 // declare and

    assign value let count = 0; // create reactive copy $: countCopy = count; // re-assign value count = count + 1; // log value console.log({ count, countCopy }); $: SPECIAL REACTIVE OPERATOR (NOT PART OF VANILLA JAVASCRIPT)
  15. RELATION TO DOM SYNCHRONIZATION 25 let value = 0; let

    valueInDOM = value; // 🎉 `value` and `valueInDOM` are in sync console.log({ value, valueInDOM }); value = value + 1; valueInDOM = value; // manual re-render necessary // 😕 `value` and `valueInDOM` are NOT in sync console.log({ value, valueInDOM });
  16. A STORY 26 let mother = "My child is "

    let baby = "innocent" let parentalBond = mother + baby console.log(parentalBond) // "My child is innocent" baby = "independent" console.log(baby) // "independent" console.log(parentalBond) // "My child is innocent" 💘 REACTIVENESS CREATES A BOND
  17. 27 UI AS A FUNCTION OF STATE MODERN FRONTEND DEVELOPMENT

    https://docs. fl utter.dev/development/data-and-backend/state-mgmt/declarative
  18. 1. LET’S TIDY UP 28 const render = function(val) {

    document.getElementById("number").innerHTML = val; } // render default value render(0); const onClick = () => { // 1. update state state = state + 1; // 2. re-render user interface render(state); };
  19. STATE CHANGE LEADS TO RE-RENDER 29 state = { value:

    undefined, setValue: function (val) { // 1. set state value this.value = val; // 2. render user interface this.render(); }, render: function () { document.getElementById("number").innerHTML = this.value; }, }; const onClick = () => { state.setValue(state.value + 1); // state change leads to re-render! }; // set default value state.setValue(0);
  20. STATE CHANGE LEADS TO RE-RENDER 30 state = { value:

    undefined, setValue: function (val) { // 1. set state value this.value = val; // 2. render user interface this.render(); }, render: function () { document.getElementById("number").innerHTML = this.value; }, }; const onClick = () => { state.setValue(state.value + 1); // state change leads to re-render! }; // set default value state.setValue(0);
  21. 31 state = { value: undefined, setValue: function (val) {

    // 1. set state value this.value = val; // 2. render user interface this.render(); }, render: function () { document.getElementById("number").innerHTML = this.value; }, }; const onClick = () => { state.setValue(state.value + 1); // state change leads to re-render! }; // set default value state.setValue(0); STATE CHANGE LEADS TO RE-RENDER
  22. DERIVED VALUE 33 let count = 0; let double =

    count * 2; // I want doubled to be in sync with count // 🎉 `count` and `doubled` are in sync console.log({ count, double }); count = count + 1; double = count * 2; // manual re-run of computation necessary! // 😕 `count` and `doubled` are NOT in sync console.log({ count, double });
  23. DERIVED VALUE 34 state = { value: undefined, double: undefined,

    setValue: function (val) { // 1. set both state values this.value = val; this.double = val * 2; // 2. render user interface this.render(); }, render: function () { document.getElementById("number").innerHTML = this.value; // Also update the `double` value in the UI document.getElementById("double").innerHTML = this.double; }, }; const onClick = () => { state.setValue(state.value + 1); }; // set default value state.setValue(0);
  24. 35 state = { // ... render: function () {

    document.getElementById("number").innerHTML = this.value; // 😰: I have to remember to also update the double value in the UI document.getElementById("double").innerHTML = this.double; }, // ... }; DERIVED VALUE
  25. 2. EVENTUALLY RENDERED UI NOT EASILY IDENTIFIABLE FROM CODE. 37

    state = { value: undefined, double: undefined, setValue: function (val) { // 1. set state values this.value = val; this.double = val * 2; // 2. render user interface this.render(); }, render: function () { document.getElementById("number").innerHTML = this.value; document.getElementById("double").innerHTML = this.double; // add congrats message if (this.value === 5) { let titleNode = document.createElement("p"); titleNode.id = "congrats"; titleNode.innerHTML = "Congratulations 🎉 "; const body = document.querySelector("body"); body.insertBefore(titleNode, body.childNodes[0]); } }, }; const onClick = () => { state.setValue(state.value + 1); }; // set default value state.setValue(0);
  26. 2. EVENTUALLY RENDERED UI NOT EASILY IDENTIFIABLE FROM CODE. 38

    state = { // ... render: function () { document.getElementById("number").innerHTML = this._value_internal; // 😰: I have to remember to also update the double value in the UI document.getElementById("double").innerHTML = this._double_internal; // add congrats message if (this._value_internal === 5) { let titleNode = document.createElement("p"); titleNode.id = "congrats"; titleNode.innerHTML = "Congratulations 🎉"; const body = document.querySelector("body"); body.insertBefore(titleNode, body.childNodes[0]); } }, // ... };
  27. 2. EVENTUALLY RENDERED UI NOT EASILY IDENTIFIABLE FROM CODE. 39

    <body> <p id="number"></p> <div>Double: <span id="double"></span></div> <button onclick="onClick()">Click to increment</button> </body> HTML <body> <p id="congrats">Congratulations 🎉</p> <p id=“number"></p> <div>Double: <span id="double"></span></div> <button onclick="onClick()">Click to increment</button> </body> DOM AFTER COUNT >= 5
  28. 2. EVENTUALLY RENDERED UI NOT EASILY IDENTIFIABLE FROM CODE. 40

    <body> <p id="number"></p> <div>Double: <span id="double"></span></div> <button onclick="onClick()">Click to increment count</ button> <script src="./index.js"></script> </body> // add congrats message if (count === 5) { let titleNode = document.createElement("p"); titleNode.id = "congrats"; titleNode.innerHTML = "Congratulations 🎉"; const body = document.querySelector("body"); body.insertBefore(titleNode, body.childNodes[0]); } HTML JAVASCRIPT 👉 LOGIC IS BUILT AROUND DOM MANIPULATION AND NOT AROUND STATE UPDATES. <body> <p id="congrats">Congratulations 🎉</p> <p id=“number"></p> <div>Double: <span id="double"></span></div> <button onclick="onClick()">Click to increment count</ button> <script src="./index.js"></script> </body> DOM AFTER COUNT > 5
  29. NO MORE IMPERATIVE DOM MANIPULATIONS! 41 // add congrats message

    if (count === 5) { let titleNode = document.createElement("p"); titleNode.id = "congrats"; titleNode.innerHTML = "Congratulations 🎉"; const body = document.querySelector("body"); body.insertBefore(titleNode, body.childNodes[0]); } JAVASCRIPT LET’S BUILD UIS IN A DECLARATIVE WAY
  30. 43 https://media.istockphoto.com/photos/teenagers-messy-room-picture-id1087233556?k=20&m=1087233556&s=612x612&w=0&h=GJ6_asZveN4P4M65pAysJWRnLf9ajxgKyhPoZsebRo8= // add congrats message if (count === 5)

    { let titleNode = document.createElement("p"); titleNode.id = "congrats"; titleNode.innerHTML = "Congratulations 🎉"; const body = document.querySelector("body"); body.insertBefore(titleNode, body.childNodes[0]); } JAVASCRIPT
  31. FROM IMPERATIVE TO DECLARATIVE 45 <!-- ... --> {#if count

    >= 5} <p>Congratulations! 🎉</p> {/if} <!-- ... --> SVELTE // ... if (this._value_internal === 5) { let titleNode = document.createElement("p"); titleNode.id = "congrats"; titleNode.innerHTML = "Congratulations 🎉"; const body = document.querySelector("body"); body.insertBefore( titleNode, body.childNodes[0] ); } // ...
  32. REACT (REACTIVE AND DECLARATIVE) 48 const App = () =>

    { return ( <> <h1>No value yet</h1> <button> Click to increment </button> </> ); }; const rootNode = document.getElementById("root"); ReactDOM.render(<App />, rootNode);
  33. REACT (REACTIVE AND DECLARATIVE) 49 const App = () =>

    { const [value, setValue] = React.useState(0); // set default value const onClick = () => { setValue(value + 1); // state change leads to re-render! }; return ( <> <h1>{value}</h1> <button onClick={onClick}>Click to re-render</button> </> ); }; const rootNode = document.getElementById("root"); ReactDOM.render(<App />, rootNode);
  34. REACT (REACTIVE AND DECLARATIVE) 50 const App = () =>

    { const [value, setValue] = React.useState(0); // set default value const double = value * 2; // derived value const onClick = () => { setValue(value + 1) // state change leads to re-render! } return ( <> {value >= 5 ? 'Congratulations 🎉' : null} <h1>{value}</h1> <p>Double: <span>{double}</span></p> <button onClick={onClick}> Click to increment </button> </> ); }; const rootNode = document.getElementById("root"); ReactDOM.render(<App />, rootNode); VIRTUAL DOM MAKES REACT DECLARATIVE USESTATE HOOK MAKES REACT REACTIVE
  35. REACT STYLE (IN VANILLA JS) 52 state = { value:

    undefined, double: undefined, setValue: function (val) { // 1. set both state values this.value = val; this.double = val * 2; // 2. render user interface this.render(); }, render: function () { document.getElementById("number").innerHTML = this.value; document.getElementById("double").innerHTML = this.double; }, }; const onClick = () => { state.setValue(state.value + 1); }; // set default value state.setValue(0);
  36. TRULY REACTIVE VALUE (IN VANILLA JS) 53 state = {

    _value_internal: undefined, _double_internal: undefined, set value(val) { // 1. set both state values this._value_internal = val; this._double_internal = val * 2; // 2. render user interface this.render(); }, render: function () { document.getElementById("number").innerHTML = this._value_internal; document.getElementById("double").innerHTML = this._double_internal; }, get value() { return this._value_internal; }, }; const onClick = () => { state.value = state.value + 1; }; // set default value state.value = 0;
  37. REACT VS. SVELTE (IN VANILLA JS) 54 SETTER FUNCTION ->

    REACTIVE // ... const onClick = () => { state.value = state.value + 1; }; // ... // ... const onClick = () => { state.setValue(state.value + 1); }; // ... REACTIVE ASSIGNMENT
  38. REACT VS. SVELTE (IN VANILLA JS) 55 SETTER FUNCTION ->

    REACTIVE // ... const onClick = () => { state.value = state.value + 1; }; // ... // ... const onClick = () => { state.setValue(state.value + 1); }; // ... REACTIVE ASSIGNMENT
  39. REACT VS. SVELTE 56 SETTER FUNCTION -> REACTIVE // ...

    const onClick = () => { state.value = state.value + 1; }; // ... // ... const onClick = () => { state.setValue(state.value + 1); }; // ... REACTIVE ASSIGNMENT const onClick = () => { setValue(value + 1); }; const onClick = () => { value = value + 1; }
  40. SVELTE (TRULY REACTIVE AND DECLARATIVE) 59 <script> let value =

    0; const onClick = () => { value = value + 1; } </script> <h1>{value}</h1> <button on:click={onClick}> Click to increment </button>
  41. SVELTE (DERIVED VALUE WITH THE DESTINY OPERATOR) 60 <script> let

    value = 0; $: double = value * 2; function onClick() { value = value + 1; } </script> <h1>{value}</h1> <p> Double: <span>{double}</span> </p> <button on:click={onClick}> Click to increment </button>
  42. SVELTE (CONDITIONAL) 61 <script> let value = 0; $: double

    = value * 2; function onClick() { value = value + 1; } </script> {#if value >= 5} <p>Congratulations! 🎉</p> {/if} <h1>{value}</h1> <p> Double: <span>{double}</span> </p> <button on:click={onClick}> Click to increment </button>
  43. REACT VS. SVELTE 62 REACT SVELTE {#if value >= 5}

    <p>Congratulations! 🎉</p> {/if} <h1>{value}</h1> <p> Double: <span>{double}</span> </p> <button on:click={onClick}> Click to increment </button> return ( <> {value >= 5 ? 'Congratulations 🎉' : null} <h1>{value}</h1> <p>Double: <span>{double}</span></p> <button onClick={onClick}> Click to increment </button> </> );
  44. FLUTTER QUIZ 63 return ViewB( color: red, child: ViewC(...), )

    IMPERATIVE DECLARATIVE b.setColor(red) b.clearChildren() ViewC c3 = new ViewC(...) b.add(c3) https://docs. fl utter.dev/get-started/ fl utter-for/declarative Let’s say we have a view b which we want to build and style:
  45. FLUTTER QUIZ 64 return ViewB( color: red, child: ViewC(...), )

    IMPERATIVE b.setColor(red) b.clearChildren() ViewC c3 = new ViewC(...) b.add(c3) https://docs. fl utter.dev/get-started/ fl utter-for/declarative Let’s say we have a view b which we want to build and style: <ViewB style={{ color: 'red' }}> <ViewC {...} /> </ViewB>; DECLARATIVE