Skip to content

Instantly share code, notes, and snippets.

@jimthedev
Last active July 23, 2020 11:49
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jimthedev/b0fbccec1b1e43fb547900c5bbf3fe17 to your computer and use it in GitHub Desktop.
Save jimthedev/b0fbccec1b1e43fb547900c5bbf3fe17 to your computer and use it in GitHub Desktop.
use web component hook for react
import { useCustomElement } from "./useCustomElement";
// My regular react app that is using a web component /
// custom element inside it.
// Notice that we have some handlers, some state, etc.
function Component() {
const [txt, setTxt] = React.useState("init")
function handleClick() {
setTxt("clicked")
}
function handleLeave() {
setTxt("out")
}
function handleEnter() {
setTxt("enter")
}
return (
<div>
<my-web-component
{...useCustomElement({
onClick: handleClick,
onMouseLeave: handleLeave,
onMouseEnter: handleEnter,
title: "Title",
text: txt,
subtext: "I am some subtext"
})}
/>
</div>
)
}
export function useCustomElement(items) {
const ref = React.useRef();
// Events are tricky in wc in react. We need to manually
// attach listeners. We can't rely on synthetic events.
React.useEffect(() => {
matchProperties(
{
handler: (eventType, value) => {
return ref.current.addEventListener(eventType, value);
},
property: (key, value) => {
return (ref.current[key] = value);
}
},
items
);
function matchProperties(matchers, properties) {
for (let pair of Object.entries(items)) {
const [key, value] = pair;
if (
key[0] === "o" &&
key[1] === "n" &&
key[2] == key[2].toUpperCase()
) {
if (typeof matchers.handler === "function") {
const eventType = key.substring(2, key.length).toLowerCase();
matchers.handler(eventType, value);
}
} else {
if (typeof matchers.property === "function") {
matchers.property(key, value);
}
}
}
}
return () => {
matchProperties(
{
handler: (eventType, value) => {
return ref.current.removeEventListener(eventType, value);
}
},
items
);
};
}, []);
// Deal with attributes that aren't events
const entries = Object.entries(items).filter(
([key, val]) => typeof items[key].indexOf !== "undefined"
);
const remainder = entries.reduce((a, item) => {
const [key, value] = item;
return {
...a,
[key]: value
};
}, {});
const result = { ref, ...remainder };
return result;
}
@jimthedev
Copy link
Author

@sslotsky awesome. Thank you for the feedback!

  1. I will work on the on syntax you described. One thing I didn’t want to have to do was maintain a list of all possible events. The camel casing gets a bit strange and unfortunately mouseleave and other eventTypes aren’t camel cased. Tradeoffs exist but certainly we can play around with this. I agree my personal preference is still to find a way to use the onMouseLeave syntax but without an extensive camel casing map I am not sure how to make it happen.

2/3 You are correct, these should likely be props in some (or many) cases. I was thinking about adding in square bracket syntax alla Angular but perhaps there is a better solution here that involves data type checking and/or checking for the existence of a prop. Camel translation again becomes a question but I will look and see what others are doing here.

Perhaps the name of this thing should be useCustomElement instead of useWebComponent.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment