0.9.0 • Published 2 years ago

@tybys/reactivuety v0.9.0

Weekly downloads
50
License
MIT
Repository
github
Last release
2 years ago

reactivuety

Write react in vue way.

API Documentation

中文

Install

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>

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 onChange event of <Input> and <Textarea> is native, not react's.

0.9.0

2 years ago

0.8.0

3 years ago

0.7.0

3 years ago

0.6.0

3 years ago

0.5.6

3 years ago

0.5.5

3 years ago

0.5.4

3 years ago

0.5.3

3 years ago

0.5.0

3 years ago

0.5.2

3 years ago

0.5.1

3 years ago

0.4.0

3 years ago

0.3.0

3 years ago

0.2.0

3 years ago

0.1.0

3 years ago