Skip to content

Commit e765fa2

Browse files
author
Guillaume Chau
committed
fix: reactivity issues, closes #34 #38
1 parent c31b92b commit e765fa2

File tree

2 files changed

+73
-25
lines changed

2 files changed

+73
-25
lines changed

src/components/MeteorData.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export default {
2626

2727
render (h) {
2828
let result = this.$scopedSlots.default({
29-
data: this.meteorData,
29+
data: this.$data.$meteor.data.meteorData,
3030
})
3131
if (Array.isArray(result)) {
3232
result = result.concat(this.$slots.default)

src/index.js

Lines changed: 72 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -58,26 +58,56 @@ export default {
5858
return result
5959
}
6060

61+
function firstPrepare () {
62+
prepare.call(this)
63+
Object.defineProperty(this, '$subReady', {
64+
get: () => this.$data.$meteor.subs,
65+
enumerable: true,
66+
configurable: true,
67+
})
68+
proxyData.call(this)
69+
}
70+
6171
function prepare () {
6272
this._trackerHandles = []
6373
this._subsAutorun = {}
6474
this._subs = {}
75+
}
76+
77+
function proxyData () {
78+
const initData = this.$_meteorInitData = {}
79+
let meteor = this.$options.meteor
6580

66-
// First launch
67-
if (this._meteorLaunch == null) {
68-
this._meteorLaunch = 0
81+
if (meteor) {
82+
// Reactive data
83+
for (let key in meteor) {
84+
if (key.charAt(0) !== '$') {
85+
proxyKey.call(this, key)
86+
87+
const func = meteor[key]
88+
89+
if (meteor.$lazy && typeof func === 'function') {
90+
initData[key] = getResult(func.call(this))
91+
}
92+
}
93+
}
94+
}
95+
}
6996

70-
Object.defineProperty(this, '$subReady', {
71-
get: () => this.$data.$meteor.subs,
72-
enumerable: true,
73-
configurable: true,
74-
})
97+
function proxyKey (key) {
98+
if (hasProperty(this, key)) {
99+
throw Error(`Meteor data '${key}': Property already used in the component methods or prototype.`)
75100
}
101+
102+
Object.defineProperty(this, key, {
103+
get: () => this.$data.$meteor.data[key],
104+
enumerable: true,
105+
configurable: true,
106+
})
76107
}
77108

78109
function launch () {
79110
this._meteorActive = true
80-
this._meteorLaunch++
81111

82112
let meteor = this.$options.meteor
83113

@@ -109,18 +139,18 @@ export default {
109139
data () {
110140
return {
111141
$meteor: {
112-
data: {},
142+
data: this.$_meteorInitData,
113143
subs: {},
114144
},
115145
}
116146
},
117147

118148
...vueVersion === '1' ? {
119-
init: prepare,
149+
init: firstPrepare,
120150
} : {},
121151

122152
...vueVersion === '2' ? {
123-
beforeCreate: prepare,
153+
beforeCreate: firstPrepare,
124154
} : {},
125155

126156
created () {
@@ -234,22 +264,20 @@ export default {
234264
this._meteorActive = false
235265
},
236266

237-
$addMeteorData (key, func) {
267+
$addMeteorData (key, func, proxy = false) {
238268
if (typeof func === 'function') {
239269
func = func.bind(this)
240270
} else {
241271
throw Error(`Meteor data '${key}': You must provide a function which returns the result.`)
242272
}
243273

244-
if (hasProperty(this.$data, key) || hasProperty(this.$props, key) || (hasProperty(this, key) && this._meteorLaunch === 1)) {
245-
throw Error(`Meteor data '${key}': Property already used in the component data, props or other.`)
246-
}
274+
if (proxy) {
275+
if (hasProperty(this.$data, key) || hasProperty(this.$props, key)) {
276+
throw Error(`Meteor data '${key}': Property already used in the component data or props.`)
277+
}
247278

248-
Object.defineProperty(this, key, {
249-
get: () => this.$data.$meteor.data[key],
250-
enumerable: true,
251-
configurable: true,
252-
})
279+
proxyKey.call(this, key)
280+
}
253281

254282
// Function run
255283
const setResult = result => {
@@ -282,6 +310,7 @@ export default {
282310
},
283311

284312
$addComputed (key, watcher) {
313+
if (watcher.getter.vuex) return
285314
let computation, autorunMethod
286315
const autorun = (cb) => {
287316
if (!computation) {
@@ -290,7 +319,20 @@ export default {
290319
computation = autorunMethod(computation => {
291320
dirty = true
292321
watcher.value = getResult(cb.call(this))
293-
watcher.deps.forEach(dep => dep.notify())
322+
// Call watcher callback
323+
const get = watcher.get
324+
watcher.get = () => watcher.value
325+
watcher.run()
326+
watcher.get = get
327+
// Notify watchers subscribed in dependencies
328+
for (const dep of watcher.deps) {
329+
const subs = dep.subs.slice()
330+
for (const sub of subs) {
331+
if (sub.id !== watcher.id) {
332+
sub.update()
333+
}
334+
}
335+
}
294336
dirty = false
295337
})
296338
// Update from Vue (override)
@@ -303,14 +345,20 @@ export default {
303345
return watcher.value
304346
}
305347
// Override getter to expose $autorun
306-
const func = watcher.getter
348+
const getter = watcher.getter
307349
watcher.getter = () => {
308350
autorunMethod = this.$autorun
309351
this.$autorun = autorun
310-
const result = func.call(this, this)
352+
const result = getter.call(this, this)
311353
this.$autorun = autorunMethod
312354
return result
313355
}
356+
// If watcher was created before the computed property
357+
// (for example because of a $watch)
358+
// we update the result with the getter override
359+
if (watcher.value instanceof Tracker.Computation) {
360+
watcher.run()
361+
}
314362
},
315363
},
316364
})

0 commit comments

Comments
 (0)