Last active
July 23, 2020 11:49
-
-
Save jimthedev/b0fbccec1b1e43fb547900c5bbf3fe17 to your computer and use it in GitHub Desktop.
use web component hook for react
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | |
) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@sslotsky awesome. Thank you for the feedback!
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.