Skip to content

Conversation

Sheraff
Copy link
Contributor

@Sheraff Sheraff commented Sep 2, 2025

Following TanStack/query#9604, this PR replicates the implementation of replaceEqualDeep here, so that both are more similar (avoid drifting apart over time).

There are no performance changes.

Summary by CodeRabbit

  • Performance

    • Optimized internal deep-compare and replacement logic to short-circuit equal values and avoid unnecessary recursion, improving route state updates. Users may see faster navigations and reduced CPU usage in complex scenarios.
  • Refactor

    • Simplified internal implementation while preserving existing behavior and public APIs. No functional changes or breaking changes for consumers.

Copy link
Contributor

coderabbitai bot commented Sep 2, 2025

Walkthrough

Refactors replaceEqualDeep in packages/router-core/src/utils.ts to unify object/array guards, add early short-circuits for identical values, streamline primitive/null handling, and simplify recursive assignments and equality counting. Return logic and public API remain unchanged.

Changes

Cohort / File(s) Summary
replaceEqualDeep refactor
packages/router-core/src/utils.ts
Consolidated plain-object/array guards; added early copy-through when prev===next; broadened primitive/null checks; simplified recursion and equality counting; preserved final return semantics and public interface.

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant replaceEqualDeep
  Caller->>replaceEqualDeep: (prev, next)

  alt Non-object/primitive/null on either side
    replaceEqualDeep-->>Caller: return next
  else Both plain objects or arrays
    loop Iterate entries
      alt p === n
        Note right of replaceEqualDeep: Short-circuit copy and increment equalItems
      else p or n is primitive/null
        Note right of replaceEqualDeep: Copy n without recursion
      else Both objects/arrays
        replaceEqualDeep->>replaceEqualDeep: recurse(p, n)
        Note right of replaceEqualDeep: Count equal when v === p
      end
    end
    alt sizes unchanged and all items equal
      replaceEqualDeep-->>Caller: return prev
    else
      replaceEqualDeep-->>Caller: return copied structure
    end
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

package: router-core

Suggested reviewers

  • schiller-manuel
  • SeanCassiere

Poem

I hop through stacks where objects sleep,
Comparing leaves in burrows deep—
If twins I see, I skip the creep,
And swiftly keep the prior heap.
With paws of logic, light and neat,
I prune the paths where values meet. 🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor-router-core-replace-equal-deep-unify-with-query

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

nx-cloud bot commented Sep 2, 2025

View your CI Pipeline Execution ↗ for commit ed2c481

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

☁️ Nx Cloud last updated this comment at 2025-09-02 08:10:13 UTC

Copy link

pkg-pr-new bot commented Sep 2, 2025

More templates

@tanstack/arktype-adapter

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

@tanstack/directive-functions-plugin

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

@tanstack/eslint-plugin-router

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

@tanstack/history

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

@tanstack/react-router

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

@tanstack/react-router-devtools

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

@tanstack/react-router-ssr-query

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

@tanstack/react-start

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

@tanstack/react-start-client

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

@tanstack/react-start-plugin

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

@tanstack/react-start-server

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

@tanstack/router-cli

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

@tanstack/router-core

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

@tanstack/router-devtools

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

@tanstack/router-devtools-core

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

@tanstack/router-generator

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

@tanstack/router-plugin

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

@tanstack/router-ssr-query-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-ssr-query-core@5067

@tanstack/router-utils

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

@tanstack/router-vite-plugin

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

@tanstack/server-functions-plugin

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

@tanstack/solid-router

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

@tanstack/solid-router-devtools

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

@tanstack/solid-start

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

@tanstack/solid-start-client

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

@tanstack/solid-start-plugin

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

@tanstack/solid-start-server

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

@tanstack/start-client-core

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

@tanstack/start-plugin-core

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

@tanstack/start-server-core

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

@tanstack/start-server-functions-client

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

@tanstack/start-server-functions-fetcher

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

@tanstack/start-server-functions-server

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

@tanstack/start-storage-context

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-storage-context@5067

@tanstack/valibot-adapter

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

@tanstack/virtual-file-routes

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

@tanstack/zod-adapter

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

commit: ed2c481

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 5b5f792 and ed2c481.

📒 Files selected for processing (1)
  • packages/router-core/src/utils.ts (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: Sheraff
PR: TanStack/router#5051
File: packages/router-core/src/utils.ts:310-315
Timestamp: 2025-08-30T09:12:13.852Z
Learning: In TanStack Router's deepEqual utility, using for..in instead of Object.keys() in getObjectKeys() when ignoreUndefined=true is acceptable because it's called only after isPlainObject() checks, which ensure objects have standard Object prototype chains with no inherited enumerable properties.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Preview
  • GitHub Check: Test
🔇 Additional comments (3)
packages/router-core/src/utils.ts (3)

221-221: Guard consolidation LGTM

Requiring both sides to be plain arrays or plain objects before recursing is correct and aligns with Query’s semantics. Early return keeps this fast.


245-252: Primitive/null short-circuit LGTM

The broadened check avoids unnecessary recursion for non-objects and nulls; good for both correctness and perf.


254-256: Simplified recursion and equality counting LGTM

Using v === p for equality accounting is clean and preserves structural sharing when possible.

Comment on lines +238 to +242
if (p === n) {
copy[key] = p
if (array ? i < prevSize : prev.hasOwnProperty(key)) equalItems++
continue
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Use safe hasOwnProperty check (prev.hasOwnProperty can throw or be shadowed)

Calling prev.hasOwnProperty(key) can fail for null-prototype objects and can be shadowed by an own property. Use Object.prototype.hasOwnProperty.call(prev, key) like you already do elsewhere in this file (deepEqual).

Apply this diff:

-      if (array ? i < prevSize : prev.hasOwnProperty(key)) equalItems++
+      if (array ? i < prevSize : Object.prototype.hasOwnProperty.call(prev, key))
+        equalItems++
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (p === n) {
copy[key] = p
if (array ? i < prevSize : prev.hasOwnProperty(key)) equalItems++
continue
}
if (p === n) {
copy[key] = p
if (array ? i < prevSize : Object.prototype.hasOwnProperty.call(prev, key))
equalItems++
continue
}
🤖 Prompt for AI Agents
In packages/router-core/src/utils.ts around lines 238 to 242, the code uses
prev.hasOwnProperty(key) which can be unsafe for null-prototype objects or if
hasOwnProperty is shadowed; replace that call with
Object.prototype.hasOwnProperty.call(prev, key) to perform a safe property
ownership check, keeping the surrounding logic unchanged so equalItems++ only
increments when the property is truly an own property.

@Sheraff Sheraff merged commit ad3ab7f into main Sep 2, 2025
6 checks passed
@Sheraff Sheraff deleted the refactor-router-core-replace-equal-deep-unify-with-query branch September 2, 2025 12:30
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.

1 participant