doseofted
doseofted

Reputation: 97

How to add multiple events handlers to Solid.js components on the same HTML element?

Overview

I'm new to Solid.js and am looking for a way to add multiple event handlers to the same HTML element from different components in JSX. This is simple to do in Vue but it appears that events are overridden in Solid.js depending on how the event is added. I've created an example in both where Vue behaves the way that I'd expect (adding multiple event handlers) but Solid overrides given events if more than one is provided for a single event.

You can compare behaviors with this example by toggling useDivAsComponent in Solid and useDivAsComponent in Vue. Vue behaves the same regardless of whether multiple event handlers are added to a component or HTML element. Solid's behavior changes depending on if events are added to an HTML element or another component.

Is there a way to easily add multiple event handlers to the same HTML element through different components (defined with JSX) in Solid?

Extra Details

I have a component where I'd like to add DOM event handlers to the root element. However, I'd like for someone using that component to be able to add their own DOM event handlers to the component too.

If the root element of the component is (props) => <div {...props} /> then only event handlers inside the component are used (if an end-user of the component adds their own, they will be overridden). When you look at Solid's built code, props are merged so it makes sense that only one the last handler is kept (I would expect Solid to use addEventListener instead).

However, if the root element of the component is a regular <div /> then Solid generates code where event handlers are added so both event handlers are kept. If you look at Solid's built code, Solid uses addEventListener()as I would expect.

Alternative Solutions

As a workaround, I could pass a reference to the inner component (<InnerComponent ref={ref} />) to the parent and manually add event listeners to avoid additional events being overridden (ref.addEventListener(...)). However, this isn't quite as convenient as other frameworks.

I could also make sure that the inner component where event handlers are added is a regular <div /> so that Solid's compiler generates code that adds event listeners rather than merging (and overriding) event handler props.

I'd love to find a way to add multiple events in JSX if possible.

Upvotes: 3

Views: 1476

Answers (2)

snnsnn
snnsnn

Reputation: 13698

In Solid there are multiple ways to add an event listener. Event listeners that are added as a prop like onClick are delegated meaning they are added to the document node for performance. For undelegated handlers use on:{eventName} syntax.

If you are going to add a listener on an element inside another component, refs are way to go. That is the third way and you can use addEventListener.

The way you use is suboptimal, better way would be a single, delegated listener on a common ancestor and using conditionals inside the handlers body.

Upvotes: 1

Edlingao
Edlingao

Reputation: 156

you could try onmouseleave (in lowercase), this will no longer overwrite but rather "sum" all your events as Solid-js assumes lowercase means "add a new event listener" (you can read the discussion about this Here)

in your solid-js example this will be kind of like this:

import { render } from 'solid-js/web';
import { Component, JSX } from "solid-js"
type HtmlProps = JSX.HTMLAttributes<HTMLDivElement>

const useDivAsComponent = true

const ComponentInner: Component<HtmlProps> = (props) => {
  const log = (name: string) => console.log("inner", name)
  const Div = (p: HtmlProps) => <div {...p} />
  return useDivAsComponent
     // onmouseenter, onmouseleave, instead of onMouseLeave onMouseEnter
    ? <Div {...props} onmouseenter={[log, "enter"]} onmouseleave={[log, "leave"]} />
    : <div {...props} onmouseenter={[log, "enter"]} onmouseleave={[log, "leave"]} />
}

const ComponentOuter: Component<HtmlProps> = (p) => {
  const log = (name: string) => console.log("outer", name)
  return <ComponentInner {...p} onMouseEnter={[log, "enter"]} onMouseLeave={[log, "leave"]} />
}

const style: JSX.CSSProperties = { background: "lightgrey", width: "100%", height: "100vh" }
render(
  () => <ComponentOuter style={style} class='hola'></ComponentOuter>,
  document.getElementById('app') as HTMLElement
)

I hope this helps! Cheers!

Upvotes: 3

Related Questions