Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 27 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,36 @@ const MyCounter = defineComponent(
render(<MyCounter value={10}>, el)
```

You can also merge the `render` function into `setup` function like this:

```tsx
const MyCounter = defineComponent(
// setup function in Vue, returns a render function
(props: Props) => {
const counter = ref(props.value)
const doubled = computed(() => counter.value * 2)
const inc = () => counter.value += 1

onUnmounted(() => console.log('Goodbye World'))

// render function
return () => {
return (
<div>
<div>{counter.value} x 2 = {doubled.value}</div>
<button onClick={inc}>Increase</button>
</div>
)
}
}
)
```

### Hooks

You can use it as a hook as well.
You can use it as a React hook as well.

> The `defineComponent` factory is actually a sugar to and equivalent to the following code.
> Please prefer `defineComponent` over `useSetup` because it has better performance, less re-rendering (because it collects reactivity dependencies).


```tsx
Expand Down
16 changes: 8 additions & 8 deletions examples/react-ts-vite/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,19 @@
"@vueuse/head": "^0.5.1",
"autoprefixer": "^10.2.5",
"graphql": "^15.4.0",
"pinia": "^2.0.0-alpha.8",
"postcss": "^8.2.8",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"pinia": "^2.1.7",
"postcss": "^8.4.38",
"react": "^18.3.1",
"react-dom": "^18.3.0",
"reactivue": "^0",
"tailwindcss": "^2.0.4",
"villus": "^1.0.0-rc.15"
"villus": "^3.5.0"
},
"devDependencies": {
"@types/react": "^17.0.3",
"@types/react-dom": "^17.0.3",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@vitejs/plugin-react-refresh": "1.3.1",
"@vue/composition-api": "^1.0.0-rc.6",
"@vue/composition-api": "^1.7.2",
"typescript": "^4.2.3",
"vite": "^2.1.4"
}
Expand Down
4 changes: 2 additions & 2 deletions examples/react-ts-vite/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useState } from 'react'
import './main.css'
import { render } from 'react-dom'
import { createRoot } from 'react-dom/client'
import { createApp } from 'reactivue'
import { createClient } from 'villus'
import { createPinia } from 'pinia'
Expand Down Expand Up @@ -67,4 +67,4 @@ function App(Props: { name: string }) {
)
}

render(<App name="Jane" />, document.getElementById('app'))
createRoot(document.getElementById('app')!).render(<App name="Jane" />)
31 changes: 20 additions & 11 deletions packages/reactivue/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,33 +58,42 @@
"@babel/preset-env": "^7.13.12",
"@rollup/plugin-node-resolve": "^11.2.1",
"@rollup/plugin-replace": "^2.4.2",
"@testing-library/jest-dom": "^5.11.10",
"@testing-library/react": "^11.2.6",
"@testing-library/dom": "^10.1.0",
"@testing-library/jest-dom": "^6.4.5",
"@testing-library/react": "^16.0.0",
"@types/jest": "^26.0.22",
"@types/react": "^17.0.3",
"@types/react-dom": "^17.0.3",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"babel-jest": "^26.6.3",
"cac": "^6.7.2",
"jest": "^26.6.3",
"preact": "^10.5.13",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react": "^18.3.1",
"react-dom": "^18.3.0",
"rollup": "^2.44.0",
"rollup-plugin-dts": "^3.0.1",
"rollup-plugin-terser": "^7.0.2",
"rollup-plugin-typescript2": "^0.30.0",
"rollup-plugin-typescript2": "^0.31.0",
"ts-jest": "^26.5.4",
"ts-node": "^9.1.1",
"tslib": "^2.1.0",
"typescript": "^4.2.3"
},
"peerDependencies": {
"preact": "^10.5.9",
"preact": ">=10",
"react": ">=16"
},
"dependencies": {
"@vue/reactivity": "^3.0.9",
"@vue/runtime-core": "^3.0.9",
"@vue/shared": "^3.0.9"
"@vue/reactivity": "^3.4.27",
"@vue/runtime-core": "^3.4.27",
"@vue/shared": "^3.4.27"
},
"peerDependenciesMeta": {
"preact": {
"optional": true
},
"react": {
"optional": true
}
}
}
2 changes: 1 addition & 1 deletion packages/reactivue/setupTests.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
import '@testing-library/jest-dom/extend-expect'
import '@testing-library/jest-dom'
19 changes: 6 additions & 13 deletions packages/reactivue/src/component.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
/* eslint-disable import/no-mutable-exports */
import { Ref, ReactiveEffect, ref, stop } from '@vue/reactivity'
import * as vueReactivity from '@vue/reactivity'
import { Ref, ReactiveEffect, ref, effectScope } from '@vue/reactivity'
import { invokeLifeCycle } from './lifecycle'
import { InstanceStateMap, InternalInstanceState, LifecycleHooks, EffectScope } from './types'
import { InstanceStateMap, InternalInstanceState, LifecycleHooks } from './types'

/**
* When `reactivue` dependency gets updated during development
Expand All @@ -22,9 +21,6 @@ const _vueState: InstanceStateMap = (__DEV__ && __BROWSER__ && window.__reactivu
if (__DEV__ && __BROWSER__)
window.__reactivue_state = _vueState

const effectScope: (detached?: boolean) => EffectScope = (vueReactivity as any)['effectScope']
export const usingEffectScope = typeof effectScope === 'function'

export let currentInstance: InternalInstanceState | null = null
export let currentInstanceId: number | null = null

Expand Down Expand Up @@ -60,7 +56,7 @@ export const createNewInstanceWithId = (id: number, props: any, data: Ref<any> =
hooks: {},
initialState: {},
provides: __BROWSER__ ? { ...window.__reactivue_context?.provides } : {},
scope: usingEffectScope ? effectScope() : null,
scope: effectScope(),
}
_vueState[id] = instance

Expand All @@ -70,10 +66,7 @@ export const createNewInstanceWithId = (id: number, props: any, data: Ref<any> =
export const useInstanceScope = (id: number, cb: (instance: InternalInstanceState | null) => void) => {
const prev = currentInstanceId
const instance = setCurrentInstanceId(id)
if (usingEffectScope) {
if (!instance?.isUnmounted) instance?.scope?.run(() => cb(instance))
}
else cb(instance)
if (!instance?.isUnmounted) instance?.scope.run(() => cb(instance))
setCurrentInstanceId(prev)
}

Expand All @@ -82,10 +75,10 @@ const unmount = (id: number) => {

// unregister all the computed/watch effects
for (const effect of _vueState[id].effects || [])
stop(effect)
effect.stop()

invokeLifeCycle(LifecycleHooks.UNMOUNTED, _vueState[id])
if (usingEffectScope) _vueState[id].scope!.stop()
_vueState[id].scope.stop()
_vueState[id].isUnmounted = true

// release the ref
Expand Down
4 changes: 2 additions & 2 deletions packages/reactivue/src/computed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
WritableComputedRef,
ComputedGetter,
} from '@vue/reactivity'
import { recordInstanceBoundEffect, usingEffectScope } from './component'
import { recordInstanceBoundEffect } from './component'

export function computed<T>(getter: ComputedGetter<T>): ComputedRef<T>
export function computed<T>(
Expand All @@ -15,6 +15,6 @@ export function computed<T>(
getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>,
) {
const c = _computed(getterOrOptions as any)
if (!usingEffectScope) recordInstanceBoundEffect(c.effect)
recordInstanceBoundEffect(c.effect)
return c
}
35 changes: 29 additions & 6 deletions packages/reactivue/src/defineComponent.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,35 @@
import { UnwrapRef } from '@vue/reactivity'
import { useSetup } from './useSetup'
import { useReducer } from 'react'
import { UnwrapRef, computed, markRaw, ref, toValue } from '@vue/reactivity'
import { USE_SETUP_NO_UPDATE, useSetup } from './useSetup'
import { watch } from './watch'

export function defineComponent<PropsType>(
setupAndRenderFunction: (props: PropsType) => ((() => JSX.Element) | JSX.Element),
): (props: PropsType) => JSX.Element
export function defineComponent<PropsType, State>(
setupFunction: (props: PropsType) => State,
renderFunction: (state: UnwrapRef<State>) => JSX.Element,
): (props: PropsType) => JSX.Element {
return (props: PropsType) => {
const state = useSetup(setupFunction, props)
return renderFunction(state)
): (props: PropsType) => JSX.Element
export function defineComponent(
setupFunction: (props: any) => any,
renderFunction?: (state: any) => JSX.Element,
): (props: any) => JSX.Element {
return (props) => {
const forceUpdate = useReducer(v => (v + 1) % 0xFFFFFFFF, 0)[1] as () => void
const state = useSetup((props) => {
const setupReturns = ref(setupFunction(props))

const jsxElement
= typeof renderFunction === 'function'
? computed(() => markRaw(renderFunction(setupReturns.value)))
: computed(() => markRaw(toValue(setupReturns.value)))

watch(jsxElement, () => {
if (jsxElement.effect.dirty) forceUpdate() // force update only when new jsxElement is not taken by React
}, { flush: 'post' })
return { jsxElement }
}, props, USE_SETUP_NO_UPDATE)

return state.jsxElement
}
}
4 changes: 4 additions & 0 deletions packages/reactivue/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,16 @@ export {
DebuggerEvent,
DeepReadonly,
effect,
effectScope,
enableTracking,
getCurrentScope,
isProxy,
isReactive,
isReadonly,
isRef,
ITERATE_KEY,
markRaw,
onScopeDispose,
pauseTracking,
reactive,
ReactiveEffect,
Expand All @@ -51,6 +54,7 @@ export {
toRef,
toRefs,
ToRefs,
toValue,
track,
TrackOpTypes,
trigger,
Expand Down
5 changes: 4 additions & 1 deletion packages/reactivue/src/mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export function createApp() {
const installedPlugins = new Set()

const app = (context.app = {
version: '3.0.0',
version: '3.4.0',

get config() {
return context.config
Expand Down Expand Up @@ -169,3 +169,6 @@ export function inject(
warn('inject() can only be used inside setup() or functional components.')
}
}
export function hasInjectionContext() {
return !!getCurrentInstance()
}
4 changes: 2 additions & 2 deletions packages/reactivue/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { ReactiveEffect, Ref } from '@vue/reactivity'

// from https://unpkg.com/@vue/reactivity@3.2.47/dist/reactivity.d.ts#L75-L89
export declare class EffectScope {
detached: boolean;
detached: boolean
/* Excluded from this release type: _active */
/* Excluded from this release type: effects */
/* Excluded from this release type: cleanups */
Expand Down Expand Up @@ -39,7 +39,7 @@ export interface InternalInstanceState {
hooks: Record<string, Function[]>
initialState: Record<any, any>
provides: Record<string, unknown>
scope: EffectScope | null
scope: EffectScope
}

export type InstanceStateMap = Record<number, InternalInstanceState>
Loading