Event Handlers and Logic Don't Mix

CREDIT: bertomic via Pixabay (CC0)

We talk event handling, and out comes the question: “Isn’t it redundant to call it onKeydown?”

I suppose it is. onKeydown hints at what should happen before the function is called (though we’ll need to inspect the call-site to confirm that hint), but may give us next to nothing about what the method actually does. They’re howling in outrage at the School of Useful Names.

In the particular case of event handlers, though, this sort of name seems fine. Compared with implementing the handler as an anonymous closure, naming the method will enable reuse and help identify it in stack traces. We’re already ahead, there.

But the second, bigger reason for a stupid name like onKeydown is to emphasize what handlers aren’t: business logic.

Presentation & Logic

Whether events originate from a user interface or another unit of our system, they’re input here. Without rigorous guarantees about their shape and content, handling them safely means validating, sanitizing, and extracting domain objects for consumption by the rest of the system. Only after we’ve “accepted” an event should we delegate to business logic.

Putting that into code:

function onKeydown (e) {
  if (!(e.keyCode && typeof e.keyCode === 'number')) {
    // Caller passed an invalid event. Panic.
    throw new Error('Event missing numeric .keyCode');
  } else if (e.keyCode < 65 || e.keyCode > 90) {
    // Key doesn't match [A-Z]. Ignore it.
    return;
  }

  // Cast type => string
  const char = String.fromCharCode(e.keyCode);

  // Delegate "accepted" input to business logic
  recordCharacter(char);
}

Event handlers are great places to, well, handle events. If we stick to that single responsibility, leaving business logic for another, nicely named function, we’ll have an easier time verifying that validation is behaving as intended. Plus, we decouple the underlying business logic: if multiple handlers need keyboard input, we can write a wrapper to filter incoming events and only forward “acceptable” ones:

function alphaHandler (handler) {
  return function (e) {
    if (!(e.keyCode && typeof e.keyCode === 'number')) {
      throw new Error('Event missing numeric .keyCode');
    } else if (e.keyCode < 65 || e.keyCode > 90) {
      return;
    }

    return handler(String.fromCharCode(e.keyCode)char);
  };
}

At this point, we can create any event handler we need by composing validation and business logic:

const onKeydown = alphaHandler(recordCharacter);

It’s still onKeydown, and it’s still a stupid name. For a stupid function. Exactly as it should be.

Hey, I'm RJ! For more learnings about software and management, find me @rjzaworski or sign up for my semi-regular newsletter.

Let’s keep in touch

Send me timely updates on software, product, and process.