Skip to content
This repository has been archived by the owner on Dec 5, 2024. It is now read-only.

fix: correctly proxify functions within ssrRef #561

Merged
merged 1 commit into from
Oct 11, 2021

Conversation

andrzejewsky
Copy link
Contributor

@andrzejewsky andrzejewsky commented Sep 27, 2021

Hello,

Sometimes when you create more complex SSR refs, that contain instances of a built-in constructor (Map, WeakMap, Promise, etc), it may cause errors due to proxying nested objects:

TypeError: Method Map.prototype.entries called on incompatible receiver [object Object]

This is a sort of limitation - those constructors keep the state inside of "internal fields" which Proxy cannot intercept by using get/set handlers, so we need to switch calling context of their methods.

Note:
We all know that keeping that complex objects within the reactive state is not a really good practice, but... sometimes there is no other choice, especially when it comes to 3rd party libraries or other things... so let's make it future-proof. Also, I think it partially solves this: #560

@andrzejewsky andrzejewsky changed the title fix: ssrRef cannot proxyfy constructors fix: ssrRef cannot proxify constructors Sep 27, 2021
@danielroe
Copy link
Member

@andrzejewsky My main concern is that we won't be able to stringify and pass a Map, WeakMap or Promise via payload (as stringified in the HTML). But I'd be very interested to know more about your use case. Would you suggest that client-side-only usage of these built-in constructors would still be valuable?

@andrzejewsky
Copy link
Contributor Author

@danielroe that's correct, you won't be able to hydrate built-in constructors as well as you can't proxy them properly.

My case is pretty straightforward. We have a customer that provides its own library based on classes, which have WeakMap as that class property. This class is being initialized as the initial state by using ssrRef. Then somewhere in the code, one of the methods from this class is responsible for manipulating this WeakMap and it calls has which causes an error as it was proxied by ssrRef along with the entire class. It more or less looks like this:

class TestCase {
  constructor() {
    this.state = new WeakMap()
  }

  operate(obj) {
    if (this.state.has(obj)) {
      return this.state.get(obj)
    }

    this.state.set(obj, 'test')
  }
}

<script>
export default {
  setup() {
    const test = ssrRef(null)

    onServerPrefetch(() => {
      test.value = new TestCase()
      test.value.operate({ test: 1 })
    })

    return {
      test
    }
  },
}
</script>

The line this.state.has(obj) will raise an error because WeakMap was proxied and method has loses its context.

My fix here, actually allows you to use that kind of constructors normally, however... as you noticed - you can't really hydrate them normally. Our customer is doing client-side re-creation somewhere in his library anyway so that's not a case and this fix actually won't stop the app anymore.

I suggest keeping that fix and maybe add a warning in the dev mode: if the reflected value is a function, notice the developer that may cause hydration issues - that might be helpful and make developers understand what can and cannot be stored within the refs.

@danielroe danielroe changed the title fix: ssrRef cannot proxify constructors fix: correctly proxify functions within ssrRef Oct 11, 2021
@danielroe danielroe merged commit 016ff0a into nuxt-community:main Oct 11, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants