Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use flatted instead of devalue to be able to revive objects #5117

Closed
vhf opened this issue Feb 26, 2019 · 6 comments
Closed

Use flatted instead of devalue to be able to revive objects #5117

vhf opened this issue Feb 26, 2019 · 6 comments

Comments

@vhf
Copy link

vhf commented Feb 26, 2019

What problem does this feature solve?

It is sometimes useful to save complex data (i.e. not primitive types nor POJOs) in the store.

Store data is passed from server to client in serialized form using devalue. Using flatted instead of devalue, together with some configuration mechanism, would allow reviving objects that would then be usable client-side.

For instance developers would add revivers to the store and these would be called client-side to revive serialized values.

What does the proposed changes look like?

I created an example repo which consists of two npx create-nuxt-app: https://github.com/vhf/nuxt-flatted-feature/commits/master . It's a dumb example because I wanted to keep it minimal, in a real-world use case I'm storing things like RDF dataset objects ( https://github.com/rdf-ext/rdf-dataset-indexed ) (initialized during serverIniti based on server data/request data).

In this example I'm storing an object based on:

export default class Bar {
  constructor (id = Math.random()) {
    this.id = id
  }
  transform () {
    return this.id * 2
  }
}

and the goal is to do someBarInstance.transform() in the frontend. The two apps have the same index.vue, the basic one shows an error because .transform is not a function on the devalued object, the other one uses flatted to serialize and revive the object, properly displaying .transform()'s result.

This feature request is available on Nuxt community (#c8733)
@augustsaintfreytag
Copy link

I was about to open a bug report, because the current behaviour with devalue is not obvious and can lead to serious issues in a strictly typed environment. A major problem tackled here is using data models that are then mapped as props to a component. If I wanted to check these props with the instanceof operator, it may work at one point but fail silently deeper down.

It is unexpected that what is passed in as a data model instance then comes out as an object (essentially only a dictionary mimicking the model) within the component.

As far as I've understood this, Nuxt has forked devalue. The original project does not use toJSON but the fork discussed here already does, though it only works one way. A proper integration with TypeScript includes the ability to use instanceof and preserve types. Either devalue is extended even further to allow for a fromJSON(…) or @vhf has already provided the right direction.

@justin-schroeder
Copy link
Sponsor

Agreed that it would be really nice to have some ability to reconstitute an object. At a bare minimum having some ability to declare some kind of fromJSON would be really helpful.

@aldarund
Copy link

something to consider. flatted seems 2.5x slower than devalue. On 120kb i got this results
devalue x 1,655 ops/sec ±0.48% (94 runs sampled)
flatted x 635 ops/sec ±1.45% (91 runs sampled)

@lucianholt97
Copy link

+1
Please add some way to hydrate class instances with methods after data injection from the server

@CleverLili
Copy link

I've been working on this problem over the past couple of days. I have it working for my project but want to see what you guys think of my solution.

  1. ​I had to make an additional fix in nuxt-contrib/devalue, I have a PR for this fix: devalue will return private data to the client even if toJSON is implemented. nuxt-contrib/devalue#14
  2. then I changed my model base class to implement toJSON. In my case, I add additional data such as "@classname" to let me know what class instance I need to hydrate
  3. finally, in my mixin I have I have the following code.

created()
{
  const isSsrHydration = (vm) => vm.$vnode && vm.$vnode.elm && vm.$vnode.elm.dataset && vm.$vnode.elm.dataset.fetchKey
  if (isSsrHydration(this))
  {
      const nuxtState = window.__NUXT__

      // Hydrate component
      this._fetchKey = this.$vnode.elm.dataset.fetchKey
      const data = nuxtState.fetch[this._fetchKey]

      // If fetch error
      if (data && data._error)
      {
          this.$fetchState.error = data._error
          return
      }

      // Merge data
      for (const key in data)
      {
          let value = data[key];

          if (Array.isArray(value))
          {
              for (let i = 0; i < value.length; i++)
              {
                  const valueElement = value[i];
                  //if it's not an array of models then skip it.
                  if (typeof valueElement !== 'object' || !valueElement["@class"])
                  {
                      break;
                  }

                  let model = this.$getModel(valueElement["@class"], valueElement._id);
                  model.fromJSON(valueElement);
                  value[i] = model;
              }
          }
          else if (typeof value === 'object' && value["@class"])
          {
              let model = this.$getModel(value["@class"], value._id);
              model.fromJSON(value);

              data[key] = model;
          }
      }
  }

@danielroe
Copy link
Member

Let's track in #12831.

@danielroe danielroe closed this as not planned Won't fix, can't repro, duplicate, stale Feb 15, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

10 participants