Git Product home page Git Product logo

Comments (20)

wucdbm avatar wucdbm commented on May 24, 2024 1

I think both -- some people may not like the "black magic" - If anything, at least include a link to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#description
I wasn't aware of it myself, actually, but guessed it's something like that based off of your example and found the documentation later

from vite-ssr.

frandiox avatar frandiox commented on May 24, 2024 1

@moeriki Not sure if you saw this one: initialState.urql = { toJSON: () => ssrCache.extractData() }. This doesn't modify urql exchange either and seems simpler.

from vite-ssr.

moeriki avatar moeriki commented on May 24, 2024 1

Ah good to know! I removed devalue for now. Thanks for all the feedback πŸ’›

from vite-ssr.

frandiox avatar frandiox commented on May 24, 2024

@wucdbm Hey thanks. What's your use case for this exactly?
There's a transformState hook that runs at the very end and gets the filled initialState as its first argument. This is normally used to make arbitrary modifications to the initial state and it can also be asynchronous. Therefore, I think this can be used in most situations.

Would that work for you?

from vite-ssr.

wucdbm avatar wucdbm commented on May 24, 2024

I'll have a look next monday, that could very well be something that I overlooked!

from vite-ssr.

frandiox avatar frandiox commented on May 24, 2024

@wucdbm did it work?

from vite-ssr.

wucdbm avatar wucdbm commented on May 24, 2024

@frandiox Priorities shifted and it was left behind, I'll get back to the thread as soon as I'm back to that project
It's literally the only thing left before we have a stable project bootstrap and we must evaluate it against a custom solution

from vite-ssr.

wucdbm avatar wucdbm commented on May 24, 2024

@frandiox No, unfortunately, that hook will not help, let me attemp to describe the issue

My main.ts looks like this

export default viteSSR(
    App,
    options,
    ({ app, router, initialState, axiosBaseUrl, axiosHost, request }: HookParams) => {"
    // some initialization code
    // ...
    
        const ssrCache = ssrExchange({
            isClient: !import.meta.env.SSR,
            // on the front-end, the client's cache is hydrated with the awaited state we previously set to initialState._urql,
            // so it won't do any ajax queries once the ssr-rendered content lands -- components will fetch their data via
            // this cache and avoid the network -- this also prevents loaders while network 
            // requests are running from spinning when we have the content from SSR
            initialState: !import.meta.env.SSR ? initialState._urql : undefined,,
            staleWhileRevalidate: true,
        })
        
        app.use(urql, {
            url: 'https://rickandmortyapi.com/graphql',
            exchanges: [
                dedupExchange,
                cacheExchange,
                ssrCache,
                fetchExchange,
            ],
        })

The issue here is, that in my components, I do this

<script lang="ts">
import { defineComponent, onServerPrefetch } from 'vue'
import { getCharacters } from '@/gql/queries'
import { useQuery } from '@urql/vue'

export default defineComponent({
    name: 'HelloWorldComp',
    setup() {
        const result = useQuery({ query: getCharacters })

        onServerPrefetch(async () => {
            await result
        })

        return {
            fetching: result.fetching,
            data: result.data,
            error: result.error,
        }
    },
})
</script>

The thing is, I'll have the data in the ssrCache I passed to urql earlier ONLY AFTER I have awaited all of its promises via onServerPrefetch.
So in that sense, I need to be able to, in addition to head, return a hook that will be ran by entry-server just after renderToString(app, context).then(deferred.resolve).catch(deferred.reject)

Then I am guaranteed that all of my promises will have been awaited, and then I still have access and can modify initialState, like so:

    if (ssrCache) {
        initialState._urql = ssrCache.extractData()
    }

(The reason I am not replacing initialState entirely is because I have other custom stores that hold other bits of data, these are simply fields in initialData and allows for future flexibility)

Essentially I am looking forward to a change such as

const { head, postRenderHook } =
      (hook &&
renderToString(app, context).then(deferred.resolve).catch(deferred.reject)
const body = await deferred.promise
postRenderHook && postRenderHook() // << this should do the magic trick if the user of the library has exported this postRenderHook from their main.ts hook

PS I just modified my code exporting a hook like above, and modified the library's vendor files to add postRenderHook && postRenderHook() as per above, and it works flawlessly, my data is now attached to the document as it should!

from vite-ssr.

frandiox avatar frandiox commented on May 24, 2024

@wucdbm Yeah I think I understand the use case. I've done this with Apollo before so I think it should work the same way with URQL.

I think this can work with transformState without adding any more hooks, since transformState is basically for doing this, transforming state after the app is rendered in SSR. Can you test something like this?

export default viteSSR(
  App,
  {
    routes,
    transformState(state, defaultTransformer) {
      if (import.meta.env.SSR) {
        // Make cache serializable in SSR
        state.urqlCache = state.urqlCache.extractData()
      }

      // Continue default serialization/deserializaion
      return defaultTransformer(state)
    },
  },
  ({ app, initialState }) => {
    // Main initialization hook
    
    initialState.urqlCache = ssrExchange({
        isClient: !import.meta.env.SSR,
        initialState: !import.meta.env.SSR ? initialState.urqlCache : undefined,
        staleWhileRevalidate: true,
    })
    
    app.use(urql, {
        url: 'https://rickandmortyapi.com/graphql',
        exchanges: [
            dedupExchange,
            cacheExchange,
            initialState.urqlCache,
            fetchExchange,
        ],
    })
  }
)

The key here is to save the ssrCache in the initial state object so you have access to it later in the transformState to serialize it.

There are other examples that follow this strategy with apollo and vue-query.

Please tell me if something like that works for you.

from vite-ssr.

wucdbm avatar wucdbm commented on May 24, 2024

@frandiox That'd definitely work, yes, but looks ugly -- IMO we shouldn't mess with business objects attached to a container that contains pure state only
On one hand, with my solution it'd need some more documentation or people to browse the source code to figure things out (I've done a lot of that on this and other libraries), on the other though, as a project evolves and becomes more complex, this hacking around may end up a huge shotgun wound in your foot

It's your call ultimately, but I'd keep the state object and other containers that implement business logic completely separate

from vite-ssr.

wucdbm avatar wucdbm commented on May 24, 2024

@frandiox Just to confirm, your solution works fine

from vite-ssr.

frandiox avatar frandiox commented on May 24, 2024

The problem in returning that hook is that it's not isometric because it would not be used in the browser.
It would be nice if the cache instance implemented a .toJSON method that is automatically called by JSON.stringify πŸ€”

Regardless, I'll give it a thought anyway since it's always possible to improve the API to something that is easier to understand.

Closing this for now since the main issues is solved πŸ‘

from vite-ssr.

frandiox avatar frandiox commented on May 24, 2024

@wucdbm One more thing, can you try something like this?

  ({ app, initialState, isClient }) => {
    // Main initialization hook
    
    const ssrCache = ssrExchange({
        isClient,
        staleWhileRevalidate: true,
        initialState: initialState._urql, // undefined in SSR
    })

    if (import.meta.env.SSR) {
        ssrCache.toJSON = () => ssrCache.extractData()  // or ssrCache.toJSON = ssrCache.extractData , not sure
        initialState._urql = ssrCache

        // edit -- I think this should also work:
        // initialState._urql = { toJSON: () => ssrCache.extractData() }
    }
    
    app.use(urql, {
        url: 'https://rickandmortyapi.com/graphql',
        exchanges: [
            dedupExchange,
            cacheExchange,
            ssrCache,
            fetchExchange,
        ],
    })
  }

Not sure if this is easier but at least it doesn't need to define transformState πŸ€”

from vite-ssr.

wucdbm avatar wucdbm commented on May 24, 2024

Yup, the one in the comment with toJSON is actually great

PS Very cool, thank you very much!

from vite-ssr.

frandiox avatar frandiox commented on May 24, 2024

@wucdbm Do you think that would be a better fit for official examples? πŸ€” (instead of using transformState hook)

from vite-ssr.

moeriki avatar moeriki commented on May 24, 2024

Another way.

    Object.defineProperty(initialState, 'urql', {
      enumerable: true,
      get() {
        return ssrCache.extractData();
      },
    });

I prefer it slightly over the other solution for it doesn't modify the urql exchange.

from vite-ssr.

moeriki avatar moeriki commented on May 24, 2024

Ah yes indeed a very good solution as well.

Unfortunately this doesn't seem to work when using devalue for serialisation.

Found this issue. Rich-Harris/devalue#15

from vite-ssr.

frandiox avatar frandiox commented on May 24, 2024

@moeriki Ah right, this is only for JSON states. I think devalue outputs a JS object or function instead.
Out of curiosity, why do you need devalue? Are you serializing Map/Set, regexps or something complex?

from vite-ssr.

moeriki avatar moeriki commented on May 24, 2024

Maybe no good reason at all. I figured there were security concerns for JSON.stringify.

I'm POCing a stack for an upcoming project. Only small test POJO payloads.

from vite-ssr.

frandiox avatar frandiox commented on May 24, 2024

@moeriki Ah I see. Vite SSR is applying some security measures though.

from vite-ssr.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.