Skip to content

Commit

Permalink
fix(restore): fix store.restore not working with ref.
Browse files Browse the repository at this point in the history
  • Loading branch information
vikiboss committed Sep 12, 2024
1 parent 296b59f commit 116ec41
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 11 deletions.
4 changes: 2 additions & 2 deletions packages/reactive/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { hasRef } from '../vanilla/ref.js'
import { isRef } from '../vanilla/ref.js'
import { reactFastCompare } from './react-fast-compare.js'

export type ExpandType<T> = {
Expand Down Expand Up @@ -29,7 +29,7 @@ export function createObjectFromPrototype<T extends object>(target: T): T {
export function canProxy(x: unknown) {
return (
isObject(x) &&
!hasRef(x) &&
!isRef(x) &&
(Array.isArray(x) || !(Symbol.iterator in x)) &&
!(x instanceof WeakMap) &&
!(x instanceof WeakSet) &&
Expand Down
3 changes: 2 additions & 1 deletion packages/reactive/src/vanilla/create.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { proxy } from './proxy.js'
import { withSnapshot } from '../enhancers/vanilla/with-snapshot.js'
import { withSubscribe } from '../enhancers/vanilla/with-subscribe.js'
import { deepCloneWithRef } from './ref.js'

import type { SubscribeListener } from './subscribe.js'
import type { WithSnapshotContributes } from '../enhancers/vanilla/with-snapshot.js'
Expand Down Expand Up @@ -50,7 +51,7 @@ export function createVanilla<State extends object>(
const proxyState = proxy(initState)

function restore() {
const clonedState = structuredClone(initState)
const clonedState = deepCloneWithRef(initState)

for (const key of Object.keys(clonedState)) {
proxyState[key as keyof State] = clonedState[key as keyof State]
Expand Down
4 changes: 2 additions & 2 deletions packages/reactive/src/vanilla/proxy.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { LISTENERS, REACTIVE, SNAPSHOT, canProxy, createObjectFromPrototype, isObject } from '../utils/index.js'
import { snapshot } from './snapshot.js'
import { hasRef } from './ref.js'
import { isRef } from './ref.js'

let globalVersion = 1

Expand Down Expand Up @@ -58,7 +58,7 @@ export function proxy<State extends object>(initState: State, parentProps: Prope

const value: any = Reflect.get(target, key, receiver)

if (hasRef(value)) {
if (isRef(value)) {
nextSnapshot[key as keyof State] = value
} else if (value?.[REACTIVE]) {
nextSnapshot[key as keyof State] = snapshot(value)
Expand Down
37 changes: 33 additions & 4 deletions packages/reactive/src/vanilla/ref.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { describe, expect, it } from 'vitest'

import { hasRef, ref } from './ref.js'
import { isRef, ref, deepCloneWithRef } from './ref.js'

describe('ref', () => {
it('should return the same object', () => {
Expand All @@ -13,10 +12,40 @@ describe('ref', () => {
it('should has ref', () => {
const obj = { a: 1 }
const refObj = ref(obj)
expect(hasRef(refObj)).toBe(true)
expect(isRef(refObj)).toBe(true)
})

it('should not be ref', () => {
expect(hasRef({})).toBe(false)
expect(isRef({})).toBe(false)
})
})

describe('deepCloneWithRef', () => {
it('should deep clone an object without refs', () => {
const obj = { a: 1, b: { c: 2 } }
const clonedObj = deepCloneWithRef(obj)

expect(clonedObj).toEqual(obj)
expect(clonedObj).not.toBe(obj)
expect(clonedObj.b).not.toBe(obj.b)
})

it('should deep clone an object with refs', () => {
const obj = { a: 1, b: ref({ c: 2 }) }
const clonedObj = deepCloneWithRef(obj)

expect(clonedObj).toEqual(obj)
expect(clonedObj).not.toBe(obj)
expect(clonedObj.b).toBe(obj.b)
})

it('should handle nested refs correctly', () => {
const nestedRef = ref({ c: 2 })
const obj = { a: 1, b: { d: nestedRef } }
const clonedObj = deepCloneWithRef(obj)

expect(clonedObj).toEqual(obj)
expect(clonedObj).not.toBe(obj)
expect(clonedObj.b.d).toBe(nestedRef)
})
})
26 changes: 24 additions & 2 deletions packages/reactive/src/vanilla/ref.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,32 @@
import { createObjectFromPrototype, isObject } from '../utils'

const internal_refSet = new WeakSet()

export function ref<T extends object>(o: T): T {
internal_refSet.add(o)
return o as T
return o
}

export function hasRef<T extends object>(k: T) {
export function isRef<T extends object>(k: T): boolean {
return internal_refSet.has(k)
}

export function deepCloneWithRef<State extends object>(initialState: State): State {
const cloned: State = createObjectFromPrototype(initialState)

for (const key in initialState) {
const value = initialState[key as keyof State]

if (isObject(value)) {
if (isRef(value)) {
cloned[key as keyof State] = value
} else {
cloned[key as keyof State] = deepCloneWithRef(value)
}
} else {
cloned[key as keyof State] = value
}
}

return cloned
}

0 comments on commit 116ec41

Please sign in to comment.