mitosis
mitosis copied to clipboard
Usage of `useRef` is broken for `lit`
I am interested in helping provide a fix!
Yes
Which generators are impacted?
- [ ] All
- [ ] Angular
- [X] HTML
- [ ] Qwik
- [ ] React
- [ ] React-Native
- [ ] Solid
- [ ] Stencil
- [ ] Svelte
- [ ] Vue
- [X] Web components
Reproduction case
https://mitosis.builder.io/?outputTab=DYSwLkA%3D&code=JYWwDg9gTgLgBAbzhAdgWQgVxTANMlAVTABMBDGAU30wGdKAlSgMxvoGUZpK4BfOZlAgg4AcgACAI0zAANiUpQAdMAgB6EMC61gtUQCh9MAJ5geAYQizotOAF5E%2BuHDBRQZKMYD8ALji0YNxQAcyd%2FSgBjVHJPX39A4BD9XkNEqihmMgieAEkolAAFITBbBDDgfL8AoNDnHQAvSjjRAA89OAAfMRASUU64FEwQSUUwkzNm2itgXv7RWUTKA2cPYDIAGTIR2TjqxNq4KOsoXYSk5yPodkjoj28qs9CU%2FUoWyFg4BUzMWXhmbAiMFUKDgeVQAApXBASn4wYVirQAJSOC6oAJwAAWEACTGY9jgdEYLAAPAAJAAqaHWABFgAA3ACiskoIEoOAAfODBrJZIjDKiUOiAhQeA5CZxuOCys46pQYJZjrRwZcoLQ%2FAqbMjpTLnMA8eCAIRYnEsLVhHXOKByzBQFDmmUpC1wPVwZVWGxKVzuTxmp3OY0wXFKALGZnBuVFaGKEzg0QAWjjKrjXpAd1E%2BBVtE9blTPvtzn4lFk9BRfoDQZDYatIAgdMokbMsGMsYTSZTab5TueTpdbsV4fyMWMvrL2MDLGDJjD9BgDejzfzFvjifdUDj9EHadwi51mYHt08O7gnYtheLPG1TvLE8rlCU1dr9eK85bK%2BO65uKCHohPOsdDu3As%2BTCVAMGwGBwXBZE7HZUs6hgEVw3lVclUvGV208PwoRKJQVUAi0NwPYwsIRXDV2uTdPHwoCwl4YDnFQYhyCoSDoNgtDhSoJCNVVKVFww4iXFIvDF0Ir87hI6EsxVCiiOovgT14fAAG1sOk1d8DUsjjlk8TPAAXXouArRgG0QXBe1iRIelF1WMg41kLYizsBAtLszZtn%2FHUrWYFzr2YLy4HZRdiVoOkDidOlgEoAB3AAhCAWjsUQAAY4DSgAmAAWOBsuWP1mDkWRkpQVAlkXdliTUMLgmCnUqusuk6uPZJ9CAA%3D%3D%3D
Expected Behaviour
For the following input:
import { onMount, onUpdate, useRef, useStore } from '@builder.io/mitosis'
type Colors = {
primary?: string
secondary?: string
}
interface IconProps {
icon: string
size?: 'xs' | 'md' | number
type?: 'solid' | 'line'
ariaLabel?: string
color?: string
colorSecondary?: string
}
export default function Icon(props: IconProps) {
const hostRef = useRef<HTMLDivElement>(null)
const state = useStore({
setColors(colors: Colors) {
if (!hostRef) {
return
}
if (colors.primary) {
hostRef.style.setProperty('--color-primary', colors.primary)
} else {
hostRef.style.removeProperty('--color-primary')
}
if (colors.secondary) {
hostRef.style.setProperty(
'--color-secondary',
colors.secondary
)
} else {
hostRef.style.removeProperty('--color-secondary')
}
},
})
onMount(() => {
state.setColors({
primary: props.color,
secondary: props.colorSecondary,
})
})
onUpdate(() => {
state.setColors({
primary: props.color,
secondary: props.colorSecondary,
})
}, [props.color, props.colorSecondary])
return (
<div
aria-label={props.ariaLabel}
ref={hostRef}
>
<svg
viewBox='0 0 24 24'
fill='none'
></svg>
</div>
)
}
the compiler should output:
import { LitElement, html, css } from "lit";
import { customElement, property, state, query } from "lit/decorators.js";
type Colors = {
primary?: string;
secondary?: string;
};
interface IconProps {
icon: string;
size?: "xs" | "md" | number;
type?: "solid" | "line";
ariaLabel?: string;
color?: string;
colorSecondary?: string;
}
@customElement("my-icon")
export default class Icon extends LitElement {
createRenderRoot() {
return this;
}
@query('[ref="hostRef"]')
hostRef!: HTMLElement;
@property() ariaLabel: any;
@property() color: any;
@property() colorSecondary: any;
setColors(colors: Colors) {
if (!this.hostRef) {
return;
}
if (colors.primary) {
this.hostRef.style.setProperty("--color-primary", colors.primary);
} else {
this.hostRef.style.removeProperty("--color-primary");
}
if (colors.secondary) {
this.hostRef.style.setProperty("--color-secondary", colors.secondary);
} else {
this.hostRef.style.removeProperty("--color-secondary");
}
}
connectedCallback() {
this.setColors({
primary: this.color,
secondary: this.colorSecondary,
});
}
updated() {
this.setColors({
primary: this.color,
secondary: this.colorSecondary,
});
}
render() {
return html`
<div aria-label="${this.ariaLabel}" ref="hostRef">
<svg viewBox="0 0 24 24" fill="none"></svg>
</div>
`;
}
}
Actual Behaviour
For the following input:
import { onMount, onUpdate, useRef, useStore } from '@builder.io/mitosis'
type Colors = {
primary?: string
secondary?: string
}
interface IconProps {
icon: string
size?: 'xs' | 'md' | number
type?: 'solid' | 'line'
ariaLabel?: string
color?: string
colorSecondary?: string
}
export default function Icon(props: IconProps) {
const hostRef = useRef<HTMLDivElement>(null)
const state = useStore({
setColors(colors: Colors) {
if (!hostRef) {
return
}
if (colors.primary) {
hostRef.style.setProperty('--color-primary', colors.primary)
} else {
hostRef.style.removeProperty('--color-primary')
}
if (colors.secondary) {
hostRef.style.setProperty(
'--color-secondary',
colors.secondary
)
} else {
hostRef.style.removeProperty('--color-secondary')
}
},
})
onMount(() => {
state.setColors({
primary: props.color,
secondary: props.colorSecondary,
})
})
onUpdate(() => {
state.setColors({
primary: props.color,
secondary: props.colorSecondary,
})
}, [props.color, props.colorSecondary])
return (
<div
aria-label={props.ariaLabel}
ref={hostRef}
>
<svg
viewBox='0 0 24 24'
fill='none'
></svg>
</div>
)
}
the compiler outputs:
import { LitElement, html, css } from "lit";
import { customElement, property, state, query } from "lit/decorators.js";
type Colors = {
primary?: string;
secondary?: string;
};
interface IconProps {
icon: string;
size?: "xs" | "md" | number;
type?: "solid" | "line";
ariaLabel?: string;
color?: string;
colorSecondary?: string;
}
@customElement("my-icon")
export default class Icon extends LitElement {
createRenderRoot() {
return this;
}
@query('[ref="hostRef"]')
hostRef!: HTMLElement;
@property() ariaLabel: any;
@property() color: any;
@property() colorSecondary: any;
setColors(colors: Colors) {
if (!hostRef) {
return;
}
if (colors.primary) {
hostRef.style.setProperty("--color-primary", colors.primary);
} else {
hostRef.style.removeProperty("--color-primary");
}
if (colors.secondary) {
hostRef.style.setProperty("--color-secondary", colors.secondary);
} else {
hostRef.style.removeProperty("--color-secondary");
}
}
connectedCallback() {
this.setColors({
primary: this.color,
secondary: this.colorSecondary,
});
}
updated() {
this.setColors({
primary: this.color,
secondary: this.colorSecondary,
});
}
render() {
return html`
<div aria-label="${this.ariaLabel}" ref="hostRef">
<svg viewBox="0 0 24 24" fill="none"></svg>
</div>
`;
}
}
Additional Information
Basically, the compiler should add this
before the useRef
reference.
This is the line that should be doing that: https://github.com/BuilderIO/mitosis/blob/d7e1c999b57316a061ef4f4b5d8cd635d790cf39/packages/core/src/generators/lit/generate.ts#L140
There seems to be a bug where it's not acting properly 🤔