Custom event compile incorrect in child component
kingzez opened this issue · 6 comments
I am interested in helping provide a fix!
Yes,need guide
Which generators are impacted?
- All
- Angular
- HTML
- Preact
- Qwik
- React
- React-Native
- Solid
- Stencil
- Svelte
- Vue
- Web components
Reproduction case
Expected Behaviour
import { useStore } from '@builder.io/mitosis'
import { Input as PaInput } from '../input'
export default function Stepper(props) {
const state = useStore({
handleBlur(event: FocusEvent) {
},
handleChange(event: string) {
console.log('change', event)
},
})
return (
<PaInput
onFocus={(event) => props.onFocus(event)}
onChange={(event) => state.handleChange(event)}
onBlur={(event) => state.handleBlur(event)}
/>
)
}
PInput
is Mitosis Child Component, onFocus
, onChange
, onBlur
is custom event,
- In Vue, custom event as props pass to component not @event, child component should also compile to props,
- In other framework not correct except "React camp"
<pa-input
onFocus="onFocus($event)"
onChange="handleChange($event)"
onBlur="handleBlur($event)"
></pa-input>
Actual Behaviour
Compile it into the Custom Event writing method of the corresponding framework, not the props writing method
Additional Information
No response
This would be a duplicate of #833. There's a bit of a long discussion around it, and I need to find the time to dive deeper into what a solution would look like.
Yes, they are somewhat similar, but they can also be grouped together. I'm a little curious about whether there is such a usage scenario in the builder sdk, using a Child component in a Component.
Haven't had an issue with the lack of Vue's events feature in Mitosis. It seems like just syntactic sugar/a nice-to-have feature, there's nothing wrong with passing onFocus
events to Vue. It's "react-style" but nothing breaks IMO.
No No No, this is not just syntax sugar. This problem prevents us from reusing existing components in mitosis new components, which is a fatal bug. You may not understand the example above, the focus is on the compilation of custom events in child components, not the syntax sugar in #833.
Now using mitosis compiled basic components
Basic component Input
mitosis code
// input.lite.tsx
import { useStore } from '@builder.io/mitosis'
export default function Input(props) {
const state = useStore({
handleFocus(event) {
props.onFocus && props.onFocus(event.target?.value)
},
handleChange(event) {
props.onChange && props.onChange(event.target?.value)
},
handleBlur(event) {
props.onBlur && props.onBlur(event.target?.value)
},
})
return (
<input
onFocus={(event) => state.handleFocus(event)}
onChange={(event) => state.handleChange(event)}
onBlur={(event) => state.handleBlur(event)}
/>
)
}
compiled vue
<!-- input.vue -->
<template>
<input @focus="handleFocus($event)" @input="handleChange($event)" @blur="handleBlur($event)" />
</template>
<script>
import { defineComponent } from 'vue'
export default defineComponent({
name: 'pa-input',
props: ['onFocus', 'onChange', 'onBlur'],
methods: {
handleFocus(event) {
this.onFocus && this.onFocus(event.target?.value)
},
handleChange(event) {
this.onChange && this.onChange(event.target?.value)
},
handleBlur(event) {
this.onBlur && this.onBlur(event.target?.value)
},
},
})
</script>
Usage scenario one
This is how we use the Input
component in a vue project. onFocus
, onChange
, and onBlur
are 'react-style'
events passed into the component as props
, which is no problem.
<!-- Home.vue -->
<template>
<pa-input
:onFocus="onFocus($event)"
:onChange="handleChange($event)"
:onBlur="handleBlur($event)"
></pa-input>
</template>
<script>
import { defineComponent } from 'vue'
import { Input as PaInput } from './input.vue'
export default defineComponent({
components: { PaInput: PaInput },
methods: {
handleBlur(event) {},
handleChange(event) {
console.log('change', event)
},
},
})
</script>
Usage scenario two
Use the basic component Input
in a new component. In mitosis, a new component Stepper
uses the Input
component, reusing our own component. Similarly, onFocus
, onChange
, and onBlur
events are passed into the component as props
, because that's how they're defined in our child component Input
. So far, there is no problem, and then we compile to react and vue.
mitosis code
// stepper.lite.tsx
import { useStore } from '@builder.io/mitosis'
import { Input as PaInput } from './input.lite'
export default function Stepper(props) {
const state = useStore({
handleStepperFocus(event) {},
handleStepperChange(event) {},
handleStepperBlur(event) {},
})
return (
<PaInput
onFocus={(event) => state.handleStepperFocus(event)}
onChange={(event) => state.handleStepperChange(event)}
onBlur={(event) => state.handleStepperBlur(event)}
/>
)
}
compiled react
// stepper.tsx
import * as React from 'react'
import { Input as PaInput } from './input'
function Stepper(props) {
function handleStepperFocus(event) {}
function handleStepperChange(event) {}
function handleStepperBlur(event) {}
return (
<PaInput
onFocus={(event) => handleStepperFocus(event)}
onChange={(event) => handleStepperChange(event)}
onBlur={(event) => handleStepperBlur(event)}
/>
)
}
export default Stepper
compiled vue
<!-- stepper.vue -->
<template>
<pa-input
@focus="handleStepperFocus($event)"
@change="handleStepperChange($event)"
@blur="handleStepperBlur($event)"
></pa-input>
</template>
<script>
import { defineComponent } from 'vue'
import { Input as PaInput } from './input.vue'
export default defineComponent({
name: 'stepper',
components: { PaInput: PaInput },
methods: {
handleStepperFocus(event) {},
handleStepperChange(event) {},
handleStepperBlur(event) {},
},
})
</script>
The above is the compilation result of react and vue:
- In react,
onFocus
,onChange
, andonBlur
events are passed into the component asprops
, which is no problem. - In vue,
onFocus
,onChange
, andonBlur
events should also be passed into the component asprops
, consistent with Usage scenario one, but in the actual compilation result, they become@focus
,@change
, and@blur
, which is a problem.
The correct compilation result of vue should be as follows:
<!-- stepper.vue -->
<template>
<pa-input
+ :onFocus="handleStepperFocus($event)"
+ :onChange="handleStepperChange($event)"
+ :onBlur="handleStepperBlur($event)"
- @focus="handleStepperFocus($event)"
- @change="handleStepperChange($event)"
- @blur="handleStepperBlur($event)"
></pa-input>
</template>
<script>
import { defineComponent } from 'vue'
import { Input as PaInput } from './input.vue'
export default defineComponent({
name: 'stepper',
components: { PaInput: PaInput },
methods: {
handleStepperFocus(event) {},
handleStepperChange(event) {},
handleStepperBlur(event) {},
},
})
</script>
This could be the cause of the problem:
https://github.com/BuilderIO/mitosis/blob/main/packages/core/src/generators/vue/blocks.ts#L320-L350
Add condition only VALID_HTML_TAGS.includes(node.name)
meet this, and other target should also add this.
if (key.startsWith('on') && VALID_HTML_TAGS.includes(node.name)) {
@samijaber Please take a good look at this. Am I right or wrong?