Skip to content

Instantly share code, notes, and snippets.

@mturley
Last active June 25, 2019 21:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mturley/f18bd61e1ee009182d7dee756ada6773 to your computer and use it in GitHub Desktop.
Save mturley/f18bd61e1ee009182d7dee756ada6773 to your computer and use it in GitHub Desktop.
Imperative vs. Declarative Programming: The Value of React for Developers

Imperative vs Declarative Programming

The Value of React for Developers

Writing code for what we want the page to do, not how we want the browser to do it.

Basic concepts

I've been working on how to try and explain the fundamental difference between the following two concepts of how programming the interactions on a web page can be done. It's important to grasp both in order to understand the main benefit of React from a developer's perspective. There are also design benefits of React such as the ability to reuse and compose isolated components, but that is beyond the scope of this document.

The two programming styles at play here are:

  • Imperative programming: The word "imperative" here implies "commanding" the computer to do something. We have to directly tell the browser exactly how to do every little thing we need it to do. This is "the old way", and it's what you do with vanilla JavaScript or something like jQuery.
  • Declarative programming: This is a layer above imperative programming, where we instead "declare" what we want the results to be after the computer does something. Our tools (e.g. React) figure out the how for us automatically. React is built with imperative code on the inside that we don't have to care about from the outside.

HTML is a declarative language

Forget about JavaScript for a moment. Here's an important fact: HTML on its own is a declarative language. In an HTML file, you can declare something like:

<section>
  <h1>Heading</h1>
  <p>Paragraph</p>
</section>

When a browser reads this HTML, it will figure out these imperative steps for you and execute them:

Create a section element.
Create a heading element of level 1.
Set the inner text of the heading element to "Heading".
Place the heading element into the section element.
Create a paragraph element.
Set the inner text of the paragraph element to "Paragraph".
Place the paragraph element into the section element.
Place the section element into the document.
Display the document on the screen.

This is a perfect example of the difference between these two concepts, just for displaying the structure of a page. In short, HTML is a declarative abstraction built around a web browser's imperative display engine. It abstracts away the "how" so you only have to worry about the "what".

Now, here's a more complicated example of what we might need to worry about when considering a user's interaction with the page and implementing its effects on the structure of that page.

JavaScript is an imperative language

Imagine a simple web page that contains the following:

  • A list of labelled checkboxes, each row of which changes to a different color when it is selected.
  • A heading at the bottom with summary text like "0 of 4 selected" which updates when the checkboxes change.
  • A "Select All" button which should be disabled if all checkboxes are already selected.
  • A "Select None" button which should be disabled if no checkboxes are selected.

In order to implement this with plain imperative JavaScript (e.g. with jQuery), these are the instructions we would need to give the browser:

  • In our HTML, we declare:
    • There are 4 row elements each containing a checkbox which is unchecked.
    • There is some summary text which reads "0 of 4 selected".
    • There is a "Select All" button which is enabled.
    • There is a "Select None" button which is disabled.
  • In our JavaScript, we write code to take each of these actions when events occur:
    • When a checkbox changes from unchecked to checked:
      • Find the row element containing the checkbox and add the "selected" class to it.
      • Find all the checkbox elements in the list and count how many are checked and not checked.
      • Find the summary text element and update it with the checked number and the total number.
      • Find the "Select None" button element and enable it if it was disabled.
      • If all checkboxes are now checked, find the "Select All" button element and disable it.
    • When a checkbox changes from checked to unchecked:
      • Find the row element containing the checkbox and remove the "selected" class from it.
      • Find all the checkbox elements in the list and count how many are checked and not checked.
      • Find the summary text element and update it with the checked number and the total number.
      • Find the "Select All" button element and enable it if it was disabled.
      • If all checkboxes are now unchecked, find the "Select None" button element and disable it.
    • When the "Select All" button is clicked:
      • Find all the checkbox elements in the list and check them all.
      • Find all the row elements in the list and add the "selected" class to them.
      • Find the summary text element and update it.
      • Find the "Select All" button and disable it.
      • Find the "Select None" button and enable it.
    • When the "Select None" button is clicked:
      • Find all the checkbox elements in the list and uncheck them all.
      • Find all the row elements in the list and remove the "selected" class from them.
      • Find the summary text element and update it.
      • Find the "Select All" button and enable it.
      • Find the "Select None" button and disable it.

Wow. That's a lot, right? Well, we better remember to write code for each and every one of those things. If we forget or screw up any of those instructions, we might end up with a bug where the totals don't match the checkboxes, or a button is enabled that doesn't do anything when you click it, or a row ends up with the wrong color.

The big challenge here is that there is no single source of truth for the state of which boxes are checked. The checkboxes know whether or not they are checked, of course, but, the row styles also have to know, the summary text has to know, and the buttons have to know. All these things need to be kept in sync by hand, explicitly, imperatively.

React allows us to use a single source of truth

React is a declarative abstraction wrapped around JavaScript. Remember how HTML let us focus on the structure of a page and not the details of how the browser displays that structure? Well, when we use React, we can focus on the structure again by defining it based on a single source of truth. When that source of truth changes, React will update the structure of the page for us automagically (it will take care of the imperative steps for us).

Let's go back to our list of checkboxes in the example above. In this case, the truth we care about is simple: which checkboxes are checked? The other details on the page (what does the summary say, what color are the rows, whether or not the buttons are enabled) are simply side-effects based on this truth. So, why should they need to have their own copy of this information? They should just use the single source of truth for reference, and everything on the page should "just know" which checkboxes are checked and conduct themselves accordingly. You might say that the row elements, summary text and buttons should all be able to just automatically React to a checkbox being checked or unchecked. (See what I did there?)

In order to implement this page, these are the declarations we would need to give React:

  • There is a list of true/false values called "checkboxStates" that represents which boxes are checked.

    For example:

      checkboxStates = [false, false, true, false]
    

    This list represents the truth that we have four checkboxes, and the third one is checked.

  • For each value in checkboxStates, there is a row element which:

    • has the class "selected" if the value is true.
    • contains a checkbox which is checked if the value is true.
  • There is a summary text element which contains the text "{x} of {y} selected" where {x} is the number of true values in checkboxStates and {y} is the total number of values in checkboxStates.

  • There is a "Select All" button that is enabled if there are any false values in checkboxStates.

  • There is a "Select None" button that is enabled if there are any true values in checkboxStates.

  • When a checkbox is clicked, change its corresponding value in checkboxStates.

  • When the "Select All" button is clicked, set all values in checkboxStates to true.

  • When the "Select None" button is clicked, set all values in checkboxStates to false.

You'll notice that the last 3 items here are still imperative instructions ("when this happens, do that"), but that's the only imperative code we need to write. It's three lines of code. The rest of those bullets are declarations ("there is a...") which are now built right into the definition of the page's structure. (This is why we need to write it in JSX instead of plain HTML, because that gives us the ability to mix logic like "if" and "for each" with the HTML structure).

Remember that list of 20 imperative instructions above? For the simple price of defining our HTML inside our JavaScript, all of those come for free. React just does them for us whenever checkboxStates changes. In this code, it is now impossible for the summary to not match the checkboxes, or for the color of a row to be wrong, or for a button to be enabled when it should be disabled. There is a whole class of bugs that is now impossible for us to have in our app: sources of truth being out of sync. Everything flows down from the single source of truth, and we developers can write less code and sleep better at night.

React is awesome for a bunch of other reasons too, but this is the big one. I hope I did an okay job explaining it this time.

@lwrigh
Copy link

lwrigh commented Jun 25, 2019

Thank you for sharing this @mturley! Definitely would love to hear more about some of the differences. You're totally welcome to present on it in the next share and learn meeting if you like :)

@mturley
Copy link
Author

mturley commented Jun 25, 2019

Sure thing! Thanks for reading it @lwrigh!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment