Events

(developer.mozilla.org)

47 points | by aanthonymax 10 hours ago ago

16 comments

  • jfagnani 6 hours ago

    I think events are a bit unsung and underutilized in a lot of web projects. Events are really powerful and you can build systems with them that can replace proprietary framework features with interoperable protocols.

    Context: Components that need a context value can fire an event to request it. Any other element or listener higher in the tree can handle the event and provide a value via the event object. Event are synchronous, so you can get values synchronously. The Web Components Community Group maintains an interoperable context community protocol: https://github.com/webcomponents-cg/community-protocols/blob...

    Suspense: Components that have some pending some work, like awaiting data to render, can fire an event to signal that they have pending work. The event can carry a promise, and then a suspense-boundary-like component can handle the event and display a spinner until all the pending work in the tree below it is finished. Another protocol: https://github.com/webcomponents-cg/community-protocols/blob...

    Error boundaries: A component can fire an ErrorEvent if it fails to render, and an error boundary component can display the error or some other user affordance.

    Child-parent registration: A child component can fire an event to tell some parent that it's available. This is useful for using components as plugins. A <code-mirror> element could have children that provide language support, syntax highlight themes, etc.

    Actions: Redux-like actions can be done with events instead. You can build a nice data-down, events-up system this way with very little code and very loose coupling.

    Event buses: components can listen for events on a top-level node like document, and they'll receive every event of that type from every other dispatcher.

  • Waterluvian 9 hours ago

    I often imagine state and events as the two impulses that drive an application. I like React a lot, but a common pitfall is that it is 95% focused on state, and so you get odd cases where you end up trying to encode events as state.

    You’ll see this anywhere you see a usePrevious-like hook that you then use to determine if something changed and act on it (eg. I hold state that a robot is offline, but I want to do something special when a robot goes offline). This is inferring an event from state.

    I’ve had luck adding an event bus as a core driver of a complex react application for events I don’t want to track as state. But it always feels that it’s a bit in conflict with the state-driven nature of the application.

    • fleabitdev 6 hours ago

      Reactivity works by replaying code when its inputs have changed. Events can make this very expensive and impractical, because to properly replay event-driven code, you'd need to replay every event it's ever received.

      When we replace an event stream with an observable variable, it's like a performance optimisation: "you can ignore all of the events which came before; here's an accumulated value which summarises the entire event stream". For example, a mouse movement event listener can often be reduced to an "is hovered" flag.

      Serialising program state to plain data isn't always easy or convenient, but it's flexible enough. Reducing all events to state almost solves the problem of impure inputs to reactive functions.

      Unfortunately, reactive functions usually have impure outputs, not just impure inputs. UI components might need to play a sound, write to a file, start an animation, perform an HTTP request, or notify a parent component that the "close" button has been clicked. It's really difficult to produce instantaneous side effects if you don't have instantaneous inputs to build on.

      I can't see an obvious solution, but until we come up with one, reactive UI toolkits will continue to be ill-formed. For example, a React component <ClickCounter mouseButton> would be broken by default: clicks are delivered by events, so they're invisible to React, so the component will display an incorrect click count when the mouseButton prop changes.

    • RossBencina 3 hours ago

      A phrase that I was expecting to see is "state transition." You want to be able to specify and execute actions on particular state transitions. In your case, you want to be able to emit events from these actions.

    • johnfn 6 hours ago

      I tend to be sus of usePrevious. Not saying it doesn't have a place, but often times it's cleaner to just write the diffing code you want directly in the handler. For instance, say you have a text field, and if the value changes you want to autosave. I would just put that right in the onBlur - `if (e.currentTarget.value != text) { autosave(e.currentTarget.value) }`. If you want to debounce, I would debounce the method, not the value.

      I tend to agree with your overall assessment - your React code is not doing well if you're encoding events into state. That's why I try to avoid it! But I may be oversimplifying.

    • c-hendricks 7 hours ago

      There's a bit about effects vs events in React's own docs: https://react.dev/learn/you-might-not-need-an-effect

    • rtpg 7 hours ago

      this is what reducers are for. Though reducers tend to make you end up needing to do a bunch of stuff on async event handling which can feel _pretty_ tedious. And if you don't do the tedious way, you often end up intro'ing really hard to debug issues.

  • JSR_FDED 5 hours ago

    Why is this so weirdly prescriptive about inline event handlers?

    > Even in a single file, inline event handlers are not a good idea. One button is OK, but what if you had 100 buttons? You'd have to add 100 attributes to the file; it would quickly turn into a maintenance nightmare.

    > You should never use the HTML event handler attributes — those are outdated, and using them is bad practice.

    It’s a really good explanatory text, and then get surprisingly opinionated.

    Similarly, why is an online event handler considered a security risk? I just don’t see the difference between that and using a named function?

    • ctidd 5 hours ago

      > Similarly, why is an online event handler considered a security risk? I just don’t see the difference between that and using a named function?

      It is a vector for script injection, and should be disallowed with a strong CSP (no “unsafe-inline”).

      • JSR_FDED 4 hours ago

        Isn’t that only the case when the inline code uses untrusted user data somehow?

        Inline: alert(“Hello “+userInput) is problematic.

        Inline: alert(“Hello there”) isn’t, right?

        • sublinear 2 hours ago

          Under a properly configured CSP, allowing scripts that aren't from the same origin to inject things into the DOM is the problem.

          Both of your examples are problematic.

  • h4ch1 9 hours ago

    Using Svelte and building global state classes with $state(), $effect() has really helped with managing side-effects and surgical updates without building a custom event system which has historically added unnecessary boilerplate to many of my projects with a frontend.

    Having components bound to or using any of the $states or $derived update automatically without having to manually register event listeners, firing events, etc.

    Used to dislike runes so much initially, but working a bit more deeply with them has really made me appreciate the API changes.

  • Sephr 5 hours ago

    Protip: Make your web SDK APIs EventTargets instead of creating custom event subscription models wherever practical.

  • deterministic an hour ago

    You can think of any software (or hardware) as a state that transitions to a new state when an event happens.

    State0 -event0-> State1 -event1-> State2 etc.

    In other words, it's all a state machine.

  • aanthonymax 10 hours ago

    Event capturing and bubbling and much more