router icon indicating copy to clipboard operation
router copied to clipboard

Unexpected behaviour when returning `defer()` value without wrapping in an object

Open melv-n opened this issue 1 year ago • 2 comments

Describe the bug

When returning a deferred promise from the route loader() the promise isn't actually deferred, but it seems awaited. This can be seen on the resolved type depending on whether the defer() value was returned directly vs. when it's wrapped inside an object.

This works:

loader: () => {
  return { 
    deferredPromise: defer(new Promise<string>(() => {})),
  }
}

const data = Route.useLoaderData() // ✅ type for `data.deferredPromise` is correctly `DeferredPromise<string>`

This does not work:

loader: () => {
  return defer(new Promise<string>(() => {}))
}

const deferredPromise = Route.useLoaderData() // ❌ type for `deferredPromise` is unexpectedly `string` instead of  `DeferredPromise<string>`

Your Example Website or App

https://stackblitz.com/edit/tanstack-router-hbzw42?file=src%2Froutes%2Fposts.%24postId.tsx

Steps to Reproduce the Bug or Issue

  1. Open the routes/posts/posts.$postId.tsx file
  2. Check out the comments in the loader
  3. Observe the difference between the types from Route.useLoaderData() depending on whether a defer(...) value is returned directly vs. when it's wrapped in an object, e.g. { deferredPromise: defer(...) }

Expected behavior

I expect the loaderData value for the defer(...) value to always be a DeferredPromise<>, not the value of said DeferredPromise.

Screenshots or Videos

CleanShot 2024-06-23 at 00 17 38@2x CleanShot 2024-06-23 at 00 17 23@2x

Platform

  • OS: macOS + Arc 1.47

Additional context

No response

melv-n avatar Jun 22 '24 22:06 melv-n

does returning a "blank" DeferredPromise even work correctly at runtime?

schiller-manuel avatar Jun 23 '24 10:06 schiller-manuel

Not when directly returned, but it does when wrapped in an object. The blank promise is only for illustration/repro.

melv-n avatar Jun 23 '24 11:06 melv-n

Looked into this, when you directly return defer from the loader, at runtime it resolves it before rendering the component, therefore, the string type is correct there. This may have something to do with essentially returning a Promise from the loader, which is resolved. This is contrasted to returning an Object with defer Promises as values, where the return value of the loader is an object and not a Promise, therefore, the loaderData type correctly resolves to DeferredPromise.

To change the type signature would require changing the existing runtime behaviour. Right now, given that all the examples show the defer being returned as part of an object, I don't see this being a goal at the moment.

If you want this to be changed, please open a new issue referencing this issue asking for this change/enhancement which can then be discussed with Tanner.

Closing this for now, as the types being resolved are correct as it is what's being currently returned at runtime.

SeanCassiere avatar Sep 09 '24 01:09 SeanCassiere