Write react in vue way.
npm install @tybys/reactivuety
or
<script src="https://cdn.jsdelivr.net/npm/react/umd/react.production.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@tybys/reactivuety/dist/reactivuety.min.js"></script>
Markdown example:
Vue:
<template>
<div id="editor">
<textarea :value="input" @input="update"></textarea>
<div v-html="compiledMarkdown"></div>
</div>
</template>
<script>
import { ref, computed, defineComponent } from 'vue'
import * as marked from 'marked'
import * as debounce from 'lodash/debounce'
export default defineComponent({
setup () {
const input = ref('# hello')
const compiledMarkdown = computed(() => marked(input.value))
const update = debounce(function(e) {
input.value = e.target.value;
}, 300)
return { input, compiledMarkdown, update }
}
})
</script>
Use defineComponent
, the first argument is setup function, which returns a react renden function.
// import ...
import * as React from 'react'
import { defineComponent, ref, computed, Textarea } from '@tybys/reactivuety'
export default defineComponent((vueProps) => {
const input = ref('# hello')
const compiledMarkdown = computed(() => ({ __html: marked(input.value) }))
const update = debounce((e) => {
input.value = e.target.value
}, 300)
return (reactProps, ref) => ( // <-- returns a react renden function
// use other react hooks here
<div id="editor">
<Textarea value={input.value} onInput={update} />
<div dangerouslySetInnerHTML={compiledMarkdown.value}></div>
</div>
)
})
No bundler:
<script src="https://cdn.jsdelivr.net/npm/react/umd/react.production.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/react-dom/umd/react-dom.production.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lodash/lodash.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/marked/lib/marked.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@tybys/reactivuety/dist/reactivuety.min.js"></script>
<script>
(function () {
var defineComponent = reactivuety.defineComponent;
var ref = reactivuety.ref;
var computed = reactivuety.computed;
var Textarea = reactivuety.Textarea;
var h = React.createElement;
var debounce = _.debounce;
var MarkdownView = defineComponent(function (vueProps) {
var input = ref('# hello');
var compiledMarkdown = computed(function () {
return { __html: marked(input.value) };
});
var update = debounce(function (e) {
input.value = e.target.value;
}, 300);
return function (reactProps, ref) {
// use other react hooks here
return h('div', { id: 'editor' },
h(Textarea, { value: input.value, onInput: update }),
h('div', { dangerouslySetInnerHTML: compiledMarkdown.value })
);
};
});
ReactDOM.render(h(MarkdownView), document.body);
})();
</script>
Use defineComponent
, the first argument is setup function, which returns an object contains vue reactive objects, the second argument is a react function component render function whose with first argument is the object returned by the setup function.
// import ...
export default defineComponent((vueProps) => {
const input = ref('# hello')
const compiledMarkdown = computed(() => ({ __html: marked(input.value) }))
const update = debounce((e) => {
input.value = e.target.value
}, 300)
return { input, compiledMarkdown, update }
}, (state, reactProps, ref) => (
// use other react hooks here
<div id="editor">
<Textarea value={state.input} onInput={state.update} />
<div dangerouslySetInnerHTML={state.compiledMarkdown}></div>
</div>
))
Use useSetup
hook, the first argument is setup function, which returns an object contains vue reactive objects, the second argument is react props.
// import ...
import * as React from 'react'
import { useSetup, ref, computed, Textarea } from '@tybys/reactivuety'
export default (reactProps) => {
const state = useSetup(
(vueProps) => {
const input = ref('# hello')
const compiledMarkdown = computed(() => ({ __html: marked(input.value) }))
const update = debounce((e) => {
input.value = e.target.value
}, 300)
return { input, compiledMarkdown, update }
},
reactProps // <-- pass react props
)
// use other react hooks here
return (
<div id="editor">
<Textarea value={state.input} onInput={state.update} />
<div dangerouslySetInnerHTML={state.compiledMarkdown}></div>
</div>
)
}
Use useSetup
hook, the first argument is setup function, which returns a render function, the second argument is react props.
// import ...
export default (reactProps, refOrContext) => {
const render = useSetup(
(vueProps) => {
const input = ref('# hello')
const compiledMarkdown = computed(() => ({ __html: marked(input.value) }))
const update = debounce((e) => {
input.value = e.target.value
}, 300)
return (reactProps, refOrContext) => (
// use other react hooks here
<div id="editor">
<Textarea value={input.value} onInput={update} />
<div dangerouslySetInnerHTML={compiledMarkdown.value}></div>
</div>
)
},
reactProps
)
return render(reactProps, refOrContext)
}
Similar to vue 3.
import { nextTick, ref, defineComponent } from '@tybys/reactivuety'
export default defineComponent(() => {
const a = ref('a')
const onClick = () => {
a.value = 'b'
console.log(document.getElementById('a').innerHTML) // a
nextTick(() => {
console.log(document.getElementById('a').innerHTML) // b
})
}
return () => (<div id="a" onClick={onClick}>{a.value}</div>)
})
Similar to vue 3.
import {
onBeforeMount,
onBeforeUnmount,
onBeforeUpdate,
onErrorCaptured,
onMounted,
onRenderTracked,
onRenderTriggered,
onUnmounted,
onUpdated,
defineComponent
} from '@tybys/reactivuety'
export default defineComponent(() => {
onBeforeMount(() => {})
onBeforeUnmount(() => {})
onBeforeUpdate(() => {})
onErrorCaptured((err, type) => {}) // <-- No instance
onMounted(() => {})
onRenderTracked((e) => {})
onRenderTriggered((e) => {})
onUnmounted(() => {})
onUpdated(() => {})
// ...
})
Similar to vue 3. But no suspensible
option.
import { defineAsyncComponent } from '@tybys/reactivuety'
const MyComponent = defineAsyncComponent(() => import('./MyComponent'))
const MyComponent2 = defineAsyncComponent({
loader: () => import('./MyComponent'),
delay: 200,
loadingComponent: () => (<MyLoading />),
errorComponent: ({ error }) => (<div>{error?.message}</div>),
timeout: Infinity
onError: (error, retry, fail) => {}
})
Similar to vue 3.
In parent:
import { provide, ref, defineComponent } from '@tybys/reactivuety'
export default defineComponent(() => {
const a = ref('')
provide('a', a)
// ...
})
In children (can be deep):
import { inject, defineComponent } from '@tybys/reactivuety'
export default defineComponent(() => {
const a = inject('a')
// ...
})
Similar to vue 3.
Support <Input>
/ <Select>
/ <Option>
/ <Textarea>
import { defineComponent, ref, Input } from '@tybys/reactivuety'
export default defineComponent(() => {
const inputValue = ref('')
return () => (<Input vModel={inputValue} />) // <-- pass ref
/*
be equivalent to
return () => (<Input
value={inputValue.value} // <-- pass value
onInput={(e) => { inputValue.value = e.target.value }}
/>)
*/
})
Also support modifiers: vModel_lazy
/ vModel_number
/ vModel_trim
import { defineComponent, ref, Input } from '@tybys/reactivuety'
export default defineComponent(() => {
const inputValue = ref('')
return () => (<Input vModel_lazy={inputValue} />)
/* return () => (
<Input
value={inputValue.value}
onChange={(e) => { inputValue.value = e.target.value }}
/>
)
})
import { ref, onMounted, defineComponent } from '@tybys/reactivuety'
export default defineComponent(() => {
const a = ref(null)
onMounted(() => {
console.log(a.current) // <div>reactivuety</div>
})
return () => (<div ref={a}>reactivuety</div>)
})
-
setup function is only called once, the first argument is readonly props
Proxy
-
setup function can return:
-
object contains vue reactive object, all of them will be observed if accessed.
-
render function without props, all accessed reactive objects in the render function will be observed.
-
-
lifecycle hooks should be called in setup function.
-
inject()
should be called in setup function. -
if
provide()
is called outside of setup function, it will provide your variable to root. -
the
onChange
event of<Input>
and<Textarea>
is native, not react's.