cferdinandi / reef

A lightweight library for creating reactive, state-based components and UI.

Home Page:https://reefjs.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Lack of event support in component

jekcom opened this issue · comments

commented

First of all I'd like to appreciate and say that it is a great library, and i was looking for something like this to incorporate some reactivity to an MVC server side rendered website without blowing it with some huge framework like React. So a big thank you 👍

Now for the issue part:
One of the definitions of component is that it can encapsulate some functionality and is reusable.
For example, imagine date picker component: it has text box, and button, and some calendar popup, and you have an event onDateSelected which can be used everywhere.
On the other hand, all the behavior functionality such as triggering the calendar' validating input, picking date etc. is encapsulated within the component only and no collisions are possible.
But without the ability to handle events in a component scope, you have to handle them in a global scope, and that defeats the whole point of a component.

I actually saw a couple of issues in the repo regarding event handling inside the component, and from all of them I understood that you are a not huge fan of ReactJs style event handling and you prefer so called event delegation.
Any way, the fact that you can't handle events in a component scope, either mean that we need to add event handlers in a global scope, or if using event delegation, we need to write a lot of "if is this button or is it that button"

I actually ended up with some workaround and just listen to 'reef:render' event and call addEventListener to all the elements of the component on each render, but IMHO it should have been declared in a component scope

   document.addEventListener('reef:render', function (event) {

        let onEvent = function (target, eventName, handler) {
            let items = event.target.querySelectorAll(target);
            items.forEach(function (el) {
                el.addEventListener(eventName, handler);
            })
        }

        onEvent(".ddl-day", "change", handleDayChange);
        onEvent(".remove-day", "click", handleRemoveDay);

        .
	.
	.

  
    });

Sounds like a different tool might be a better choice for you, candidly.

Reef isn't particularly concerned with scope and encapsulation. That's not what it's for. It's sole focus is on data reactivity and DOM diffing.

If you're attaching events to elements that don't go away after the initial render, you might be better served by hooking into the reef:start event instead.

or if using event delegation, we need to write a lot of "if is this button or is it that button"

I've grown used over the years to using the delegating form of jQuery's .on() method to deal with this in a nicer way:

$('#app').on('click', 'button.some-class', function(e) {
  // only fires for clicks on a <button class="some-class">, and
  // within the function body "this" refers to the clicked button
});

Given the "vanilla JS" philosophy of Reef, I wrote a helper function that does the same thing without jQuery:

const delegatedEventListener = (selector, fn) => {
    return function (event) {
        const target = event.target;
        const matchingElt = target.closest(selector);
        if (matchingElt && event.currentTarget.contains(matchingElt)) {
            // this is a valid delegated event, call the handler with this pointing to the
            // element that matched the selector (which may be event.target or event.currentTarget
            // but is more likely somewhere between the two in the parent-child chain).
            return fn.call(matchingElt, event);
        }
    };
};

// use it like this
document.getElementById('app').addEventListener('click', delegatedEventListener('button.some-class', function(e) {
  // only fires for clicks on a <button class="some-class">, and
  // within the function body "this" refers to the clicked button
}));