Skip to content

Commit

Permalink
fix(ssr): set() twice lose reactivity (#821)
Browse files Browse the repository at this point in the history
Co-authored-by: Anthony Fu <anthonyfu117@hotmail.com>
  • Loading branch information
posva and antfu committed Oct 5, 2021
1 parent 92b7eb1 commit 416845a
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 8 deletions.
3 changes: 2 additions & 1 deletion src/reactivity/reactive.ts
Expand Up @@ -136,7 +136,8 @@ export function observe<T>(obj: T): T {
* Mock __ob__ for object recursively
*/
export function mockReactivityDeep(obj: any, seen = new Set()) {
if (seen.has(obj)) return
if (seen.has(obj) || hasOwn(obj, '__ob__') || !Object.isExtensible(obj))
return

def(obj, '__ob__', mockObserver(obj))
seen.add(obj)
Expand Down
23 changes: 16 additions & 7 deletions src/reactivity/set.ts
Expand Up @@ -24,22 +24,33 @@ export function set<T>(target: AnyObject, key: any, val: T): T {
`Cannot set reactive property on undefined, null, or primitive value: ${target}`
)
}

const ob = target.__ob__

function ssrMockReactivity() {
// in SSR, there is no __ob__. Mock for reactivity check
if (ob && isObject(val) && !hasOwn(val, '__ob__')) {
mockReactivityDeep(val)
}
}

if (isArray(target)) {
if (isValidArrayIndex(key)) {
target.length = Math.max(target.length, key)
target.splice(key, 1, val)
ssrMockReactivity()
return val
} else if (key === 'length' && (val as any) !== target.length) {
target.length = val as any
;(target as any).__ob__?.dep.notify()
ob?.dep.notify()
return val
}
}
if (key in target && !(key in Object.prototype)) {
target[key] = val
ssrMockReactivity()
return val
}
const ob = target.__ob__
if (target._isVue || (ob && ob.vmCount)) {
__DEV__ &&
warn(
Expand All @@ -48,18 +59,16 @@ export function set<T>(target: AnyObject, key: any, val: T): T {
)
return val
}

if (!ob) {
target[key] = val
return val
}

defineReactive(ob.value, key, val)
// IMPORTANT: define access control before trigger watcher
defineAccessControl(target, key, val)

// in SSR, there is no __ob__. Mock for reactivity check
if (isObject(target[key]) && !hasOwn(target[key], '__ob__')) {
mockReactivityDeep(target[key])
}
ssrMockReactivity()

ob.dep.notify()
return val
Expand Down
20 changes: 20 additions & 0 deletions test/ssr/ssrReactive.spec.ts
Expand Up @@ -57,6 +57,26 @@ describe('SSR Reactive', () => {
expect(isRaw(state)).toBe(false)
})

it('should work on objects sets with set()', () => {
const state = ref<any>({})

set(state.value, 'a', {})
expect(isReactive(state.value.a)).toBe(true)

set(state.value, 'a', {})
expect(isReactive(state.value.a)).toBe(true)
})

it('should work on arrays sets with set()', () => {
const state = ref<any>([])

set(state.value, 1, {})
expect(isReactive(state.value[1])).toBe(true)

set(state.value, 1, {})
expect(isReactive(state.value[1])).toBe(true)
})

// #550
it('props should work with set', async (done) => {
let props: any
Expand Down

0 comments on commit 416845a

Please sign in to comment.