Skip to content

chore: merge main #4283

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

Merged
merged 38 commits into from
May 30, 2025
Merged

chore: merge main #4283

merged 38 commits into from
May 30, 2025

Conversation

schiller-manuel
Copy link
Contributor

No description provided.

danecando and others added 30 commits May 12, 2025 00:37
tanstack.com has been updated to use /src instead of /app, which means
these broken asset links has to be updated too
… for TanStackRouterDevtoolsCore (#4126)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
porting from alpha to main

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
…ceEqualDeep` (#4237)

Opening this PR for discussion, I'm very open to scope changes.

This PR tackles two problems with `defaultStructuralSharing: true`:

1. symbol properties are not taken into account for "object equality"
and are not copied over when creating a new copy
2. non-enumerable properties are not taken into account for "object
equality" and are not copied over when creating a new copy

Point 1. actually is causing some of our users a headache, see
apollographql/apollo-client#12619 and
https://community.apollographql.com/t/preloadquery-with-tanstack-router-loader/9100
.

Users expect that they're able to pass an object with symbol properties
over from a loader into components, but when having
`defaultStructuralSharing` enabled, from the second object that is
passed over, it creates a copy and strips off symbol properties.
That new object without the expected symbol properties will then crash
our `useReadQuery` hook.

In other words, the first loader call returns
```js
{
    refObject: { toPromise: Function1, [secretSymbol]: ImplementationDetail1 }
}
```
the component receives
```js
{
    refObject: { toPromise: Function1, [secretSymbol]: ImplementationDetail1 }
}
```
and the second loader call returns
```js
{
    refObject: { toPromise: Function2, [secretSymbol]: ImplementationDetail2 }
}
```
but the component receives
```js
{
    refObject: { toPromise: Function2 }
}
```
`toPromise` is a different function, so it goes into the "clone" path,
but skips the `secretSymbol` property.

If we wouldn't have that `toPromise` property on there (and we're
considering removing it!), it would even be worse to debug - users would
always stay on the first `refObject` and never get the second, since
both objects would be considered equal.

On our side, passing these objects from loaders to components is
actually an intended pattern, so this is causing quite a bit of problems
- see this example from our docs (see [Initiating queries outside
React](https://www.apollographql.com/docs/react/data/suspense#initiating-queries-outside-react)):

```ts
import { useLoaderData } from 'react-router-dom';

export function loader() {
  return preloadQuery(GET_DOGS_QUERY);
}

export function RouteComponent() {
  const queryRef = useLoaderData();
  const { data } = useReadQuery(queryRef);

  return (
    // ...
  );
}
```

Problem 2. is purely hypothetical and we haven't encountered it - but it
should probably be handled in some way.

Both of these problems can be handled in two ways:

* take these "special properties" into account for comparisons and copy
them over (my naive approach here would lose the "non-enumerability" of
these properties, more code would be necessary to keep that)
* in `isPlainObject` just return `false` if an object has non-enumerable
or symbol properties. This would opt out of structural sharing for these
objects (which is probably perfectly fine)

I'd be open for either of those solutions, and also for completely
dropping the "non-enumerable" case for simplicity, but I would be very
happy if we could get *something* in to help with symbol properties.
In React Query, these values can be assumed to be serializable JSON, so
the current implementation is perfectly fine, but with client-side
loaders, users can just pass anything, and here it's causing quite the
problem.
Telling our users to situationally turn off structural sharing would be
an educational nightmare.
Custom links created by `createLink` would incorrectly infer `ref`
values for non-intrinsic components (such as those with their own `ref`
prop like the [documented
example](https://tanstack.com/router/latest/docs/framework/react/guide/custom-link#basic-example)),
which would result in the ref being "double-wrapped". That is:

Before:
```typescript
{
  // Effective LinkComponentReactProps<TComp>
  // ...
  ref?: React.Ref<React.Ref<HTMLAnchorElement> | undefined> | undefined
  // notice the ref has been "ref'ed" twice
}
```

After fix:
```typescript
{
  // Effective LinkComponentReactProps<TComp>
  // ...
  ref?: React.Ref<HTMLAnchorElement> | undefined>
}
```

This patch, the equivilant applied directly to `link.d.ts` via `yarn
patch`, has been our workaround and it appears to work well:
<details>
<summary>@tanstack-react-router-npm-1.120.3-f6c72d7c75.patch</summary>

```patch
diff --git a/dist/esm/link.d.ts b/dist/esm/link.d.ts
index f7a4dfc2678f3669ee48b97c22e4c69d2f3ff195..be0adda3bf4302466c3c4b4d73ff6c78474a237f 100644
--- a/dist/esm/link.d.ts
+++ b/dist/esm/link.d.ts
@@ -4,9 +4,9 @@ import { ValidateLinkOptions, ValidateLinkOptionsArray } from './typePrimitives.
 import * as React from 'react';
 export declare function useLinkProps<TRouter extends AnyRouter = RegisteredRouter, const TFrom extends string = string, const TTo extends string | undefined = undefined, const TMaskFrom extends string = TFrom, const TMaskTo extends string = ''>(options: UseLinkPropsOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>, forwardedRef?: React.ForwardedRef<Element>): React.ComponentPropsWithRef<'a'>;
 type UseLinkReactProps<TComp> = TComp extends keyof React.JSX.IntrinsicElements ? React.JSX.IntrinsicElements[TComp] : React.PropsWithoutRef<TComp extends React.ComponentType<infer TProps> ? TProps : never> & React.RefAttributes<TComp extends React.FC<{
-    ref: infer TRef;
+    ref: React.Ref<infer TRef>;
 }> | React.Component<{
-    ref: infer TRef;
+    ref: React.Ref<infer TRef>;
 }> ? TRef : never>;
 export type UseLinkPropsOptions<TRouter extends AnyRouter = RegisteredRouter, TFrom extends RoutePaths<TRouter['routeTree']> | string = string, TTo extends string | undefined = '.', TMaskFrom extends RoutePaths<TRouter['routeTree']> | string = TFrom, TMaskTo extends string = '.'> = ActiveLinkOptions<'a', TRouter, TFrom, TTo, TMaskFrom, TMaskTo> & UseLinkReactProps<'a'>;
 export type ActiveLinkOptions<TComp = 'a', TRouter extends AnyRouter = RegisteredRouter, TFrom extends string = string, TTo extends string | undefined = '.', TMaskFrom extends string = TFrom, TMaskTo extends string = '.'> = LinkOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo> & ActiveLinkOptionProps<TComp>;
```

</details>

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Fix code-based splitting example.
Change file name from `// src/posts.tsx` to `// src/posts.lazy.tsx`
Co-authored-by: Joshua Coffee <[email protected]>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Copy link

nx-cloud bot commented May 30, 2025

View your CI Pipeline Execution ↗ for commit 97101e0.

Command Status Duration Result
nx affected --targets=test:eslint,test:unit,tes... ✅ Succeeded 3m 26s View ↗
nx run-many --target=build --exclude=examples/*... ✅ Succeeded 49s View ↗

☁️ Nx Cloud last updated this comment at 2025-05-30 21:44:34 UTC

Copy link

pkg-pr-new bot commented May 30, 2025

More templates

@tanstack/arktype-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/arktype-adapter@4283

@tanstack/directive-functions-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/directive-functions-plugin@4283

@tanstack/eslint-plugin-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/eslint-plugin-router@4283

@tanstack/history

npm i https://pkg.pr.new/TanStack/router/@tanstack/history@4283

@tanstack/react-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router@4283

@tanstack/react-router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router-devtools@4283

@tanstack/react-router-with-query

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router-with-query@4283

@tanstack/react-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start@4283

@tanstack/react-start-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-client@4283

@tanstack/react-start-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-plugin@4283

@tanstack/react-start-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-server@4283

@tanstack/router-cli

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-cli@4283

@tanstack/router-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-core@4283

@tanstack/router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-devtools@4283

@tanstack/router-devtools-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-devtools-core@4283

@tanstack/router-generator

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-generator@4283

@tanstack/router-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-plugin@4283

@tanstack/router-utils

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-utils@4283

@tanstack/router-vite-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-vite-plugin@4283

@tanstack/server-functions-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/server-functions-plugin@4283

@tanstack/solid-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router@4283

@tanstack/solid-router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router-devtools@4283

@tanstack/solid-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start@4283

@tanstack/solid-start-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-client@4283

@tanstack/solid-start-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-plugin@4283

@tanstack/solid-start-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-server@4283

@tanstack/start-client-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-client-core@4283

@tanstack/start-plugin-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-plugin-core@4283

@tanstack/start-server-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-server-core@4283

@tanstack/start-server-functions-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-server-functions-client@4283

@tanstack/start-server-functions-fetcher

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-server-functions-fetcher@4283

@tanstack/start-server-functions-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-server-functions-server@4283

@tanstack/valibot-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/valibot-adapter@4283

@tanstack/virtual-file-routes

npm i https://pkg.pr.new/TanStack/router/@tanstack/virtual-file-routes@4283

@tanstack/zod-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/zod-adapter@4283

commit: 97101e0

@schiller-manuel schiller-manuel merged commit 6a97386 into alpha May 30, 2025
5 checks passed
@schiller-manuel schiller-manuel deleted the merge-main branch May 30, 2025 21:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.