@tybys/reactivuety v0.9.0
reactivuety
Write react in vue way.
Install
npm install @tybys/reactivuetyor
<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>Compare with Vue
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)
}Other usage
nextTick
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>)
}) Lifecycles
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(() => {})
  // ...
}) Async component
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) => {}
})Provide / Inject
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')
  // ...
})vModel
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 }}
    />
  )
})react compatible ref
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>)
})Note
- 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 - onChangeevent of- <Input>and- <Textarea>is native, not react's.