Search icon CANCEL
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Mastering JavaScript Functional Programming

You're reading from   Mastering JavaScript Functional Programming Write clean, robust, and maintainable web and server code using functional JavaScript and TypeScript

Arrow left icon
Product type Paperback
Published in Apr 2023
Publisher Packt
ISBN-13 9781804610138
Length 614 pages
Edition 3rd Edition
Languages
Arrow right icon
Author (1):
Arrow left icon
Federico Kereki Federico Kereki
Author Profile Icon Federico Kereki
Federico Kereki
Arrow right icon
View More author details
Toc

Table of Contents (17) Chapters Close

Preface 1. Chapter 1: Becoming Functional – Several Questions 2. Chapter 2: Thinking Functionally – A First Example FREE CHAPTER 3. Chapter 3: Starting Out with Functions – A Core Concept 4. Chapter 4: Behaving Properly – Pure Functions 5. Chapter 5: Programming Declaratively – A Better Style 6. Chapter 6: Producing Functions – Higher-Order Functions 7. Chapter 7: Transforming Functions – Currying and Partial Application 8. Chapter 8: Connecting Functions – Pipelining, Composition, and More 9. Chapter 9: Designing Functions – Recursion 10. Chapter 10: Ensuring Purity – Immutability 11. Chapter 11: Implementing Design Patterns – The Functional Way 12. Chapter 12: Building Better Containers – Functional Data Types 13. Answers to Questions 14. Bibliography
15. Index 16. Other Books You May Enjoy

Our problem – doing something only once

Let’s consider a simple but common situation. You have developed an e-commerce site; the user can fill their shopping cart, and in the end, they must click on a Bill me button so that their credit card will be charged. However, the user shouldn’t click twice (or more), or they will be billed several times.

The HTML part of your application might have something like this somewhere:

<button id="billButton"
    onclick="billTheUser(some, sales, data)">Bill me
      </button>

And, among the scripts, you’d have something similar to the following code:

function billTheUser(some, sales, data) {
  window.alert("Billing the user...");
  // actually bill the user
}

A bad example

Assigning the events handler directly in HTML, the way I did it, isn’t recommended. Instead, unobtrusively, you should set the handler through code. So, do as I say, not as I do!

This is a bare-bones explanation of the web page problem, but it’s enough for our purposes. Now, let’s get to thinking about ways of avoiding repeated clicks on that button. How can we manage to prevent the user from clicking more than once? That’s an interesting problem, with several possible solutions – let’s start by looking at bad ones!

How many ways can you think of to solve our problem? Let’s go over several solutions and analyze their quality.

Solution 1 – hoping for the best!

How can we solve the problem? The first solution may seem like a joke: do nothing, tell the user not to click twice, and hope for the best! Your page might look like Figure 2.1:

Figure 2.1 – An actual screenshot of a page, just warning you against clicking more than once

Figure 2.1 – An actual screenshot of a page, just warning you against clicking more than once

This is a way to weasel out of the problem; I’ve seen several websites that just warn the user about the risks of clicking more than once and do nothing to prevent the situation. So, the user got billed twice? We warned them... it’s their fault!

Your solution might simply look like the following code:

<button
  id="billButton"
  onclick="billTheUser(some, sales, data)">Bill me
</button>
<b>WARNING: PRESS ONLY ONCE, DO NOT PRESS AGAIN!!</b>

Okay, this isn’t an actual solution; let’s move on to more serious proposals.

Solution 2 – using a global flag

The solution most people would probably think of first is using some global variable to record whether the user has already clicked on the button. You define a flag named something like clicked, initialized with false. When the user clicks on the button, if clicked is false, you change it to true and execute the function; otherwise, you do nothing at all. This can be seen in the following code:

let clicked = false;
.
.
.
function billTheUser(some, sales, data) {
  if (!clicked) {
    clicked = true;
    window.alert("Billing the user...");
    // actually bill the user
  }
}

This works, but it has several problems that must be addressed:

  • You are using a global variable, and you could change its value by accident. Global variables aren’t a good idea, in JavaScript or other languages. You must also remember to re-initialize it to false when the user starts buying again. If you don’t, the user won’t be able to make a second purchase because paying will become impossible.
  • You will have difficulties testing this code because it depends on external things (that is, the clicked variable).

So, this isn’t a very good solution. Let’s keep thinking!

Solution 3 – removing the handler

We may go for a lateral kind of solution, and instead of having the function avoid repeated clicks, we might just remove the possibility of clicking altogether. The following code does just that; the first thing that billTheUser() does is remove the onclick handler from the button, so no further calls will be possible:

function billTheUser(some, sales, data) {
  document
    .getElementById("billButton")
    .onclick = null;
  window.alert("Billing the user...");
  // actually bill the user
}

This solution also has some problems:

  • The code is tightly coupled to the button, so you won’t be able to reuse it elsewhere
  • You must remember to reset the handler; otherwise, the user won’t be able to make a second purchase
  • Testing will also be more complex because you’ll have to provide some DOM elements

We can enhance this solution a bit and avoid coupling the function to the button by providing the latter’s ID as an extra argument in the call. (This idea can also be applied to some of the further solutions that we’ll see.) The HTML part would be as follows; note the extra argument to billTheUser():

<button
  id="billButton"
  onclick="billTheUser('billButton', some, sales, data)"
>Bill me
</button>

We also have to change the called function so that it will use the received buttonId value to access the corresponding button:

function billTheUser(buttonId, some, sales, data) {
  document.getElementById(buttonId).onclick = null;
  window.alert("Billing the user...");
  // actually bill the user
}

This solution is somewhat better. But, in essence, we are still using a global element – not a variable, but the onclick value. So, despite the enhancement, this isn’t a very good solution either. Let’s move on.

Solution 4 – changing the handler

A variant to the previous solution would be not to remove the click function, but to assign a new one instead. We are using functions as first-class objects here when we assign the alreadyBilled() function to the click event. The function warning the user that they have already clicked could look something like this:

function alreadyBilled() {
  window.alert("Your billing process is running; don't
    click, please.");
}

Our billTheUser() function would then be like the following code – note how instead of assigning null to the onclick handler as in the previous section, now, the alreadyBilled() function is assigned:

function billTheUser(some, sales, data) {
  document
    .getElementById("billButton")
    .onclick = alreadyBilled;
  window.alert("Billing the user...");
  // actually bill the user
}

There’s a good point to this solution; if the user clicks a second time, they’ll get a warning not to do that, but they won’t be billed again. (From the point of view of user experience, it’s better.) However, this solution still has the very same objections as the previous one (code coupled to the button, needing to reset the handler, and harder testing), so we don’t consider it quite good anyway.

Solution 5 – disabling the button

A similar idea here is instead of removing the event handler, we can disable the button so the user won’t be able to click. You might have a function such as the one shown in the following code, which does exactly that by setting the disabled attribute of the button:

function billTheUser(some, sales, data) {
  document
    .getElementById("billButton")
    .setAttribute("disabled", "true");
  window.alert("Billing the user...");
  // actually bill the user
}

This also works, but we still have objections as with the previous solutions (coupling the code to the button, needing to re-enable the button, and harder testing), so we don’t like this solution either.

Solution 6 – redefining the handler

Another idea: instead of changing anything in the button, let’s have the event handler change itself. The trick is in the second line of the following code; by assigning a new value to the billTheUser variable, we are dynamically changing what the function does! The first time you call the function, it will do its thing, but it will also change itself out of existence by giving its name to a new function:

function billTheUser(some, sales, data) {
  billTheUser = function() {};
  window.alert("Billing the user...");
  // actually bill the user
}

There’s a special trick in the solution. Functions are global, so the billTheUser=... line changes the function’s inner workings. From that point on, billTheUser will be the new (null) function. This solution is still hard to test. Even worse, how would you restore the functionality of billTheUser, setting it back to its original objective?

Solution 7 – using a local flag

We can go back to the idea of using a flag, but instead of making it global (which was our main objection to the second solution), we can use an Immediately Invoked Function Expression (IIFE), which we’ll see more about in Chapter 3, Starting Out with Functions, and Chapter 11, Implementing Design Patterns. With this, we can use a closure, so clicked will be local to the function and not visible anywhere else:

var billTheUser = (clicked => {
  return (some, sales, data) => {
    if (!clicked) {
      clicked = true;
      window.alert("Billing the user...");
      // actually bill the user
    }
  };
})(false);

This solution is along the lines of the global variable solution, but using a private, local variable is an enhancement. (Note how clicked gets its initial value from the call at the end.) The only drawback we could find is that we'll have to rework every function that needs to be called only once to work in this fashion (and, as we’ll see in the following section, our FP solution is similar to it in some ways). Okay, it’s not too hard to do, but don’t forget the Don’t Repeat Yourself (DRY), usual advice!

We have now gone through multiple ways of solving our “do something only once” problem – but as we’ve seen, they were not very good! Let’s think about the problem functionally so that we get a more general solution.

You have been reading a chapter from
Mastering JavaScript Functional Programming - Third Edition
Published in: Apr 2023
Publisher: Packt
ISBN-13: 9781804610138
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at AU $24.99/month. Cancel anytime