Skip to content

Commit

Permalink
fix: reactive for props (#547)
Browse files Browse the repository at this point in the history
* fix: reactive for props

* chore: remove uneccssary PropsReactive symbol

* refactor: createObserver

* Update src/reactivity/reactive.ts

Co-authored-by: Carlos Rodrigues <david-181@hotmail.com>

Co-authored-by: Carlos Rodrigues <david-181@hotmail.com>
  • Loading branch information
antfu and pikax committed Oct 4, 2020
1 parent 9315570 commit 4d39443
Show file tree
Hide file tree
Showing 6 changed files with 20 additions and 13 deletions.
2 changes: 1 addition & 1 deletion src/apis/watch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ function createVueWatcher(
}

// We have to monkeypatch the teardown function so Vue will run
// runCleanup() when it tears down the watcher on unmmount.
// runCleanup() when it tears down the watcher on unmounted.
function patchWatcherTeardown(watcher: VueWatcher, runCleanup: () => void) {
const _teardown = watcher.teardown
watcher.teardown = function (...args) {
Expand Down
4 changes: 2 additions & 2 deletions src/mixin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import {
resolveScopedSlots,
asVmProperty,
} from './utils/instance'
import { PropsReactive } from './utils/symbols'
import { createObserver } from './reactivity/reactive'

export function mixin(Vue: VueConstructor) {
Vue.mixin({
Expand Down Expand Up @@ -83,7 +83,7 @@ export function mixin(Vue: VueConstructor) {
const ctx = createSetupContext(vm)

// fake reactive for `toRefs(props)`
def(props, PropsReactive, true)
def(props, '__ob__', createObserver())

// resolve scopedSlots and slots to functions
resolveScopedSlots(vm, ctx.slots)
Expand Down
6 changes: 5 additions & 1 deletion src/reactivity/reactive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ function observe<T>(obj: T): T {
return observed
}

export function createObserver() {
return observe<any>({}).__ob__ || {}
}

export function shallowReactive<T extends object = any>(obj: T): T
export function shallowReactive(obj: any): any {
if (__DEV__ && !obj) {
Expand Down Expand Up @@ -255,7 +259,7 @@ export function markRaw<T extends object>(obj: T): T {
}

// set the vue observable flag at obj
const ob = (observe({}) as any).__ob__
const ob = createObserver()
ob.__raw__ = true
def(obj, '__ob__', ob)

Expand Down
7 changes: 2 additions & 5 deletions src/reactivity/ref.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Data } from '../component'
import { RefKey, PropsReactive } from '../utils/symbols'
import { RefKey } from '../utils/symbols'
import { proxy, isPlainObject, warn } from '../utils'
import { reactive, isReactive, shallowReactive } from './reactive'
import { readonlySet } from '../utils/sets'
Expand Down Expand Up @@ -114,16 +114,13 @@ export function ref(raw?: unknown) {
export function isRef<T>(value: any): value is Ref<T> {
return value instanceof RefImpl
}
function isPropObject(obj: unknown) {
return obj && typeof obj === 'object' && PropsReactive in obj
}

export function unref<T>(ref: T): T extends Ref<infer V> ? V : T {
return isRef(ref) ? (ref.value as any) : ref
}

export function toRefs<T extends Data = Data>(obj: T): ToRefs<T> {
if (__DEV__ && !isReactive(obj) && !isPropObject(obj)) {
if (__DEV__ && !isReactive(obj)) {
warn(`toRefs() expects a reactive object but received a plain one.`)
}
if (!isPlainObject(obj)) return obj as any
Expand Down
1 change: 0 additions & 1 deletion src/utils/symbols.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,3 @@ export const WatcherPostFlushQueueKey = createSymbol(

// must be a string, symbol key is ignored in reactive
export const RefKey = 'composition-api.refKey'
export const PropsReactive = '__props_reactive__'
13 changes: 10 additions & 3 deletions test/setup.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ const {
markRaw,
toRaw,
nextTick,
isReactive,
defineComponent,
onMounted,
onMounted
} = require('../src')
const { sleep } = require('./helpers/utils')

Expand Down Expand Up @@ -58,7 +59,7 @@ describe('setup', () => {
expect(vm.b).toBe('foobar')
})

it('should be overrided by data option of plain object', () => {
it('should be overridden by data option of plain object', () => {
const vm = new Vue({
setup() {
return {
Expand Down Expand Up @@ -303,8 +304,9 @@ describe('setup', () => {
expect(vm.$refs.test.b).toBe(1)
})

it('props should not be reactive', (done) => {
it('props should be reactive', (done) => {
let calls = 0
let _props
const vm = new Vue({
template: `<child :msg="msg"></child>`,
setup() {
Expand All @@ -318,6 +320,8 @@ describe('setup', () => {
template: `<span>{{ localMsg }}</span>`,
props: ['msg'],
setup(props) {
_props = props

return {
localMsg: props.msg,
computedMsg: computed(() => props.msg + ' world'),
Expand All @@ -326,6 +330,9 @@ describe('setup', () => {
},
},
}).$mount()

expect(isReactive(_props)).toBe(true)

const child = vm.$children[0]
expect(child.localMsg).toBe('hello')
expect(child.computedMsg).toBe('hello world')
Expand Down

0 comments on commit 4d39443

Please sign in to comment.