From 47cab3fd69a77f95cb791cf16fbbea2c6b0d1bda Mon Sep 17 00:00:00 2001 From: Maneesh Tewani Date: Fri, 28 Mar 2025 12:38:10 -0700 Subject: [PATCH 1/6] Fixed issue where updated variables weren't getting picked up --- .../js/default-connector/README.md | 36 ++++----- .../js/default-connector/index.cjs.js | 15 ++++ packages/react/package.json | 4 + .../data-connect/useDataConnectQuery.test.tsx | 40 ++++++++++ .../src/data-connect/useDataConnectQuery.ts | 33 +++++--- pnpm-lock.yaml | 78 +++++++++++++++++++ 6 files changed, 178 insertions(+), 28 deletions(-) diff --git a/dataconnect-sdk/js/default-connector/README.md b/dataconnect-sdk/js/default-connector/README.md index c3d1ddb..0c66142 100644 --- a/dataconnect-sdk/js/default-connector/README.md +++ b/dataconnect-sdk/js/default-connector/README.md @@ -28,7 +28,7 @@ A connector is a collection of Queries and Mutations. One SDK is generated for e You can find more information about connectors in the [Data Connect documentation](https://firebase.google.com/docs/data-connect#how-does). ```javascript -import { getDataConnect, DataConnect } from 'firebase/data-connect'; +import { getDataConnect } from 'firebase/data-connect'; import { connectorConfig } from '@dataconnect/default-connector'; const dataConnect = getDataConnect(connectorConfig); @@ -41,7 +41,7 @@ To connect to the emulator, you can use the following code. You can also follow the emulator instructions from the [Data Connect documentation](https://firebase.google.com/docs/data-connect/web-sdk#instrument-clients). ```javascript -import { connectDataConnectEmulator, getDataConnect, DataConnect } from 'firebase/data-connect'; +import { connectDataConnectEmulator, getDataConnect } from 'firebase/data-connect'; import { connectorConfig } from '@dataconnect/default-connector'; const dataConnect = getDataConnect(connectorConfig); @@ -98,7 +98,7 @@ export interface ListMoviesData { ### Using `ListMovies`'s action shortcut function ```javascript -import { getDataConnect, DataConnect } from 'firebase/data-connect'; +import { getDataConnect } from 'firebase/data-connect'; import { connectorConfig, listMovies } from '@dataconnect/default-connector'; @@ -122,7 +122,7 @@ listMovies().then((response) => { ### Using `ListMovies`'s `QueryRef` function ```javascript -import { getDataConnect, DataConnect, executeQuery } from 'firebase/data-connect'; +import { getDataConnect, executeQuery } from 'firebase/data-connect'; import { connectorConfig, listMoviesRef } from '@dataconnect/default-connector'; @@ -185,7 +185,7 @@ export interface GetMovieByIdData { ### Using `GetMovieById`'s action shortcut function ```javascript -import { getDataConnect, DataConnect } from 'firebase/data-connect'; +import { getDataConnect } from 'firebase/data-connect'; import { connectorConfig, getMovieById, GetMovieByIdVariables } from '@dataconnect/default-connector'; // The `GetMovieById` query requires an argument of type `GetMovieByIdVariables`: @@ -215,7 +215,7 @@ getMovieById(getMovieByIdVars).then((response) => { ### Using `GetMovieById`'s `QueryRef` function ```javascript -import { getDataConnect, DataConnect, executeQuery } from 'firebase/data-connect'; +import { getDataConnect, executeQuery } from 'firebase/data-connect'; import { connectorConfig, getMovieByIdRef, GetMovieByIdVariables } from '@dataconnect/default-connector'; // The `GetMovieById` query requires an argument of type `GetMovieByIdVariables`: @@ -275,7 +275,7 @@ export interface GetMetaData { ### Using `GetMeta`'s action shortcut function ```javascript -import { getDataConnect, DataConnect } from 'firebase/data-connect'; +import { getDataConnect } from 'firebase/data-connect'; import { connectorConfig, getMeta } from '@dataconnect/default-connector'; @@ -299,7 +299,7 @@ getMeta().then((response) => { ### Using `GetMeta`'s `QueryRef` function ```javascript -import { getDataConnect, DataConnect, executeQuery } from 'firebase/data-connect'; +import { getDataConnect, executeQuery } from 'firebase/data-connect'; import { connectorConfig, getMetaRef } from '@dataconnect/default-connector'; @@ -374,7 +374,7 @@ export interface CreateMovieData { ### Using `CreateMovie`'s action shortcut function ```javascript -import { getDataConnect, DataConnect } from 'firebase/data-connect'; +import { getDataConnect } from 'firebase/data-connect'; import { connectorConfig, createMovie, CreateMovieVariables } from '@dataconnect/default-connector'; // The `CreateMovie` mutation requires an argument of type `CreateMovieVariables`: @@ -406,7 +406,7 @@ createMovie(createMovieVars).then((response) => { ### Using `CreateMovie`'s `MutationRef` function ```javascript -import { getDataConnect, DataConnect, executeMutation } from 'firebase/data-connect'; +import { getDataConnect, executeMutation } from 'firebase/data-connect'; import { connectorConfig, createMovieRef, CreateMovieVariables } from '@dataconnect/default-connector'; // The `CreateMovie` mutation requires an argument of type `CreateMovieVariables`: @@ -474,7 +474,7 @@ export interface UpsertMovieData { ### Using `UpsertMovie`'s action shortcut function ```javascript -import { getDataConnect, DataConnect } from 'firebase/data-connect'; +import { getDataConnect } from 'firebase/data-connect'; import { connectorConfig, upsertMovie, UpsertMovieVariables } from '@dataconnect/default-connector'; // The `UpsertMovie` mutation requires an argument of type `UpsertMovieVariables`: @@ -506,7 +506,7 @@ upsertMovie(upsertMovieVars).then((response) => { ### Using `UpsertMovie`'s `MutationRef` function ```javascript -import { getDataConnect, DataConnect, executeMutation } from 'firebase/data-connect'; +import { getDataConnect, executeMutation } from 'firebase/data-connect'; import { connectorConfig, upsertMovieRef, UpsertMovieVariables } from '@dataconnect/default-connector'; // The `UpsertMovie` mutation requires an argument of type `UpsertMovieVariables`: @@ -572,7 +572,7 @@ export interface DeleteMovieData { ### Using `DeleteMovie`'s action shortcut function ```javascript -import { getDataConnect, DataConnect } from 'firebase/data-connect'; +import { getDataConnect } from 'firebase/data-connect'; import { connectorConfig, deleteMovie, DeleteMovieVariables } from '@dataconnect/default-connector'; // The `DeleteMovie` mutation requires an argument of type `DeleteMovieVariables`: @@ -602,7 +602,7 @@ deleteMovie(deleteMovieVars).then((response) => { ### Using `DeleteMovie`'s `MutationRef` function ```javascript -import { getDataConnect, DataConnect, executeMutation } from 'firebase/data-connect'; +import { getDataConnect, executeMutation } from 'firebase/data-connect'; import { connectorConfig, deleteMovieRef, DeleteMovieVariables } from '@dataconnect/default-connector'; // The `DeleteMovie` mutation requires an argument of type `DeleteMovieVariables`: @@ -660,7 +660,7 @@ export interface AddMetaData { ### Using `AddMeta`'s action shortcut function ```javascript -import { getDataConnect, DataConnect } from 'firebase/data-connect'; +import { getDataConnect } from 'firebase/data-connect'; import { connectorConfig, addMeta } from '@dataconnect/default-connector'; @@ -684,7 +684,7 @@ addMeta().then((response) => { ### Using `AddMeta`'s `MutationRef` function ```javascript -import { getDataConnect, DataConnect, executeMutation } from 'firebase/data-connect'; +import { getDataConnect, executeMutation } from 'firebase/data-connect'; import { connectorConfig, addMetaRef } from '@dataconnect/default-connector'; @@ -742,7 +742,7 @@ export interface DeleteMetaData { ### Using `DeleteMeta`'s action shortcut function ```javascript -import { getDataConnect, DataConnect } from 'firebase/data-connect'; +import { getDataConnect } from 'firebase/data-connect'; import { connectorConfig, deleteMeta, DeleteMetaVariables } from '@dataconnect/default-connector'; // The `DeleteMeta` mutation requires an argument of type `DeleteMetaVariables`: @@ -772,7 +772,7 @@ deleteMeta(deleteMetaVars).then((response) => { ### Using `DeleteMeta`'s `MutationRef` function ```javascript -import { getDataConnect, DataConnect, executeMutation } from 'firebase/data-connect'; +import { getDataConnect, executeMutation } from 'firebase/data-connect'; import { connectorConfig, deleteMetaRef, DeleteMetaVariables } from '@dataconnect/default-connector'; // The `DeleteMeta` mutation requires an argument of type `DeleteMetaVariables`: diff --git a/dataconnect-sdk/js/default-connector/index.cjs.js b/dataconnect-sdk/js/default-connector/index.cjs.js index 6fa0628..84b7ed0 100644 --- a/dataconnect-sdk/js/default-connector/index.cjs.js +++ b/dataconnect-sdk/js/default-connector/index.cjs.js @@ -12,62 +12,77 @@ exports.createMovieRef = function createMovieRef(dcOrVars, vars) { dcInstance._useGeneratedSdk(); return mutationRef(dcInstance, 'CreateMovie', inputVars); } + exports.createMovie = function createMovie(dcOrVars, vars) { return executeMutation(createMovieRef(dcOrVars, vars)); }; + exports.upsertMovieRef = function upsertMovieRef(dcOrVars, vars) { const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true); dcInstance._useGeneratedSdk(); return mutationRef(dcInstance, 'UpsertMovie', inputVars); } + exports.upsertMovie = function upsertMovie(dcOrVars, vars) { return executeMutation(upsertMovieRef(dcOrVars, vars)); }; + exports.deleteMovieRef = function deleteMovieRef(dcOrVars, vars) { const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true); dcInstance._useGeneratedSdk(); return mutationRef(dcInstance, 'DeleteMovie', inputVars); } + exports.deleteMovie = function deleteMovie(dcOrVars, vars) { return executeMutation(deleteMovieRef(dcOrVars, vars)); }; + exports.addMetaRef = function addMetaRef(dc) { const { dc: dcInstance} = validateArgs(connectorConfig, dc, undefined); dcInstance._useGeneratedSdk(); return mutationRef(dcInstance, 'AddMeta'); } + exports.addMeta = function addMeta(dc) { return executeMutation(addMetaRef(dc)); }; + exports.deleteMetaRef = function deleteMetaRef(dcOrVars, vars) { const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true); dcInstance._useGeneratedSdk(); return mutationRef(dcInstance, 'DeleteMeta', inputVars); } + exports.deleteMeta = function deleteMeta(dcOrVars, vars) { return executeMutation(deleteMetaRef(dcOrVars, vars)); }; + exports.listMoviesRef = function listMoviesRef(dc) { const { dc: dcInstance} = validateArgs(connectorConfig, dc, undefined); dcInstance._useGeneratedSdk(); return queryRef(dcInstance, 'ListMovies'); } + exports.listMovies = function listMovies(dc) { return executeQuery(listMoviesRef(dc)); }; + exports.getMovieByIdRef = function getMovieByIdRef(dcOrVars, vars) { const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true); dcInstance._useGeneratedSdk(); return queryRef(dcInstance, 'GetMovieById', inputVars); } + exports.getMovieById = function getMovieById(dcOrVars, vars) { return executeQuery(getMovieByIdRef(dcOrVars, vars)); }; + exports.getMetaRef = function getMetaRef(dc) { const { dc: dcInstance} = validateArgs(connectorConfig, dc, undefined); dcInstance._useGeneratedSdk(); return queryRef(dcInstance, 'GetMeta'); } + exports.getMeta = function getMeta(dc) { return executeQuery(getMetaRef(dc)); }; diff --git a/packages/react/package.json b/packages/react/package.json index cdbc12f..a1c11e2 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -46,5 +46,9 @@ "peerDependencies": { "@tanstack/react-query": "^5", "firebase": "^11.3.0" + }, + "dependencies": { + "@types/deep-equal": "^1.0.4", + "deep-equal": "^2.2.3" } } diff --git a/packages/react/src/data-connect/useDataConnectQuery.test.tsx b/packages/react/src/data-connect/useDataConnectQuery.test.tsx index 3054bb1..dfe7c92 100644 --- a/packages/react/src/data-connect/useDataConnectQuery.test.tsx +++ b/packages/react/src/data-connect/useDataConnectQuery.test.tsx @@ -269,4 +269,44 @@ describe("useDataConnectQuery", () => { { id: movieId }, ]); }); + + test("fetches new data when variables change", async () => { + const movieData1 = { + title: "tanstack query firebase 1", + genre: "library 1", + imageUrl: "https://invertase.io/1", + }; + const createdMovie = await createMovie(movieData1); + const movieData2 = { + title: "tanstack query firebase 2", + genre: "library 2", + imageUrl: "https://invertase.io/2", + }; + const createdMovie2 = await createMovie(movieData2); + + const movieId = createdMovie?.data?.movie_insert?.id; + const movieId2 = createdMovie2?.data?.movie_insert?.id; + getMovieByIdRef({ id: movieId2 }); + const ref = getMovieByIdRef({ id: movieId }); + const { result } = renderHook(() => useDataConnectQuery(ref), { + wrapper, + }); + + await waitFor(() => { + expect(result.current.isSuccess).toBe(true); + expect(result.current.data?.movie?.title).toBe(movieData1?.title); + expect(result.current.data?.movie?.genre).toBe(movieData1?.genre); + expect(result.current.data?.movie?.imageUrl).toBe(movieData1?.imageUrl); + }); + await act(async () => { + ref.variables.id = createdMovie2.data.movie_insert.id; + result.current.refetch(); + }); + await waitFor(() => { + expect(result.current.isSuccess).toBe(true); + expect(result.current.data?.movie?.title).toBe(movieData2?.title); + expect(result.current.data?.movie?.genre).toBe(movieData2?.genre); + expect(result.current.data?.movie?.imageUrl).toBe(movieData2?.imageUrl); + }); + }); }); diff --git a/packages/react/src/data-connect/useDataConnectQuery.ts b/packages/react/src/data-connect/useDataConnectQuery.ts index de0198e..5cc4324 100644 --- a/packages/react/src/data-connect/useDataConnectQuery.ts +++ b/packages/react/src/data-connect/useDataConnectQuery.ts @@ -3,6 +3,7 @@ import { type UseQueryOptions, useQuery, } from "@tanstack/react-query"; +import deepEqual from "deep-equal"; import type { FirebaseError } from "firebase/app"; import { type CallerSdkType, @@ -11,7 +12,7 @@ import { type QueryResult, executeQuery, } from "firebase/data-connect"; -import { useState } from "react"; +import { useEffect, useMemo, useState } from "react"; import type { PartialBy } from "../../utils"; import type { QueryResultRequiredRef, @@ -22,6 +23,11 @@ export type useDataConnectQueryOptions< TData = object, TError = FirebaseError, > = PartialBy, "queryFn">, "queryKey">; +function getRef( + refOrResult: QueryRef | QueryResult, +): QueryRef { + return "ref" in refOrResult ? refOrResult.ref : refOrResult; +} export function useDataConnectQuery( refOrResult: QueryRef | QueryResult, @@ -31,17 +37,24 @@ export function useDataConnectQuery( const [dataConnectResult, setDataConnectResult] = useState< QueryResultRequiredRef >("ref" in refOrResult ? refOrResult : { ref: refOrResult }); + const [ref, setRef] = useState(dataConnectResult.ref); // TODO(mtewani): in the future we should allow for users to pass in `QueryResult` objects into `initialData`. - let initialData: Data | InitialDataFunction | undefined; - const { ref } = dataConnectResult; + const [initialData] = useState( + dataConnectResult.data || options?.initialData, + ); - if ("ref" in refOrResult) { - initialData = { - ...refOrResult.data, - }; - } else { - initialData = options?.initialData; - } + useEffect(() => { + setRef((oldRef) => { + const newRef = getRef(refOrResult); + if ( + newRef.name !== oldRef.name || + !deepEqual(oldRef, newRef, { strict: true }) + ) { + return newRef; + } + return oldRef; + }); + }, [refOrResult]); // @ts-expect-error function is hidden under `DataConnect`. ref.dataConnect._setCallerSdkType(_callerSdkType); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f74bb3a..6596518 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -152,6 +152,12 @@ importers: '@tanstack/react-query': specifier: ^5 version: 5.66.9(react@19.0.0) + '@types/deep-equal': + specifier: ^1.0.4 + version: 1.0.4 + deep-equal: + specifier: ^2.2.3 + version: 2.2.3 firebase: specifier: ^11.3.0 version: 11.3.1 @@ -1358,6 +1364,9 @@ packages: '@types/aria-query@5.0.4': resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + '@types/deep-equal@1.0.4': + resolution: {integrity: sha512-tqdiS4otQP4KmY0PR3u6KbZ5EWvhNdUoS/jc93UuK23C220lOZ/9TvjfxdPcKvqwwDVtmtSCrnr0p/2dirAxkA==} + '@types/estree@1.0.6': resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} @@ -1776,6 +1785,10 @@ packages: resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} engines: {node: '>=6'} + deep-equal@2.2.3: + resolution: {integrity: sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==} + engines: {node: '>= 0.4'} + deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -1850,6 +1863,9 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} + es-get-iterator@1.1.3: + resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==} + es-iterator-helpers@1.2.1: resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==} engines: {node: '>= 0.4'} @@ -2213,6 +2229,10 @@ packages: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} + is-arguments@1.2.0: + resolution: {integrity: sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==} + engines: {node: '>= 0.4'} + is-array-buffer@3.0.5: resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} engines: {node: '>= 0.4'} @@ -2604,6 +2624,10 @@ packages: resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} engines: {node: '>= 0.4'} + object-is@1.1.6: + resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==} + engines: {node: '>= 0.4'} + object-keys@1.1.1: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} @@ -2987,6 +3011,10 @@ packages: std-env@3.8.0: resolution: {integrity: sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==} + stop-iteration-iterator@1.1.0: + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} + engines: {node: '>= 0.4'} + streamsearch@1.1.0: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} engines: {node: '>=10.0.0'} @@ -4436,6 +4464,8 @@ snapshots: '@types/aria-query@5.0.4': {} + '@types/deep-equal@1.0.4': {} + '@types/estree@1.0.6': {} '@types/json-schema@7.0.15': {} @@ -4915,6 +4945,27 @@ snapshots: deep-eql@5.0.2: {} + deep-equal@2.2.3: + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.8 + es-get-iterator: 1.1.3 + get-intrinsic: 1.3.0 + is-arguments: 1.2.0 + is-array-buffer: 3.0.5 + is-date-object: 1.1.0 + is-regex: 1.2.1 + is-shared-array-buffer: 1.0.4 + isarray: 2.0.5 + object-is: 1.1.6 + object-keys: 1.1.1 + object.assign: 4.1.7 + regexp.prototype.flags: 1.5.4 + side-channel: 1.1.0 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.18 + deep-is@0.1.4: {} defaults@1.0.4: @@ -5031,6 +5082,18 @@ snapshots: es-errors@1.3.0: {} + es-get-iterator@1.1.3: + dependencies: + call-bind: 1.0.8 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + is-arguments: 1.2.0 + is-map: 2.0.3 + is-set: 2.0.3 + is-string: 1.1.1 + isarray: 2.0.5 + stop-iteration-iterator: 1.1.0 + es-iterator-helpers@1.2.1: dependencies: call-bind: 1.0.8 @@ -5564,6 +5627,11 @@ snapshots: hasown: 2.0.2 side-channel: 1.1.0 + is-arguments@1.2.0: + dependencies: + call-bound: 1.0.3 + has-tostringtag: 1.0.2 + is-array-buffer@3.0.5: dependencies: call-bind: 1.0.8 @@ -5947,6 +6015,11 @@ snapshots: object-inspect@1.13.4: {} + object-is@1.1.6: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + object-keys@1.1.1: {} object.assign@4.1.7: @@ -6393,6 +6466,11 @@ snapshots: std-env@3.8.0: {} + stop-iteration-iterator@1.1.0: + dependencies: + es-errors: 1.3.0 + internal-slot: 1.1.0 + streamsearch@1.1.0: {} string-width@4.2.3: From 6edf44a5354c21c69b504fdf15a7dcd1e3c4c0da Mon Sep 17 00:00:00 2001 From: Maneesh Tewani Date: Fri, 28 Mar 2025 13:45:58 -0700 Subject: [PATCH 2/6] Removed unnecessary dependency --- packages/react/package.json | 6 +- .../src/data-connect/useDataConnectQuery.ts | 7 +- packages/react/src/data-connect/utils.test.ts | 42 ++++++++++ packages/react/src/data-connect/utils.ts | 33 ++++++++ pnpm-lock.yaml | 78 ------------------- 5 files changed, 79 insertions(+), 87 deletions(-) create mode 100644 packages/react/src/data-connect/utils.test.ts create mode 100644 packages/react/src/data-connect/utils.ts diff --git a/packages/react/package.json b/packages/react/package.json index a1c11e2..ffb9b87 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -1,6 +1,6 @@ { "name": "@tanstack-query-firebase/react", - "version": "1.0.7", + "version": "2.0.8", "description": "TanStack Query bindings for Firebase and React", "type": "module", "scripts": { @@ -46,9 +46,5 @@ "peerDependencies": { "@tanstack/react-query": "^5", "firebase": "^11.3.0" - }, - "dependencies": { - "@types/deep-equal": "^1.0.4", - "deep-equal": "^2.2.3" } } diff --git a/packages/react/src/data-connect/useDataConnectQuery.ts b/packages/react/src/data-connect/useDataConnectQuery.ts index 5cc4324..60fbb9f 100644 --- a/packages/react/src/data-connect/useDataConnectQuery.ts +++ b/packages/react/src/data-connect/useDataConnectQuery.ts @@ -1,9 +1,7 @@ import { - type InitialDataFunction, type UseQueryOptions, useQuery, } from "@tanstack/react-query"; -import deepEqual from "deep-equal"; import type { FirebaseError } from "firebase/app"; import { type CallerSdkType, @@ -12,12 +10,13 @@ import { type QueryResult, executeQuery, } from "firebase/data-connect"; -import { useEffect, useMemo, useState } from "react"; +import { useEffect, useState } from "react"; import type { PartialBy } from "../../utils"; import type { QueryResultRequiredRef, UseDataConnectQueryResult, } from "./types"; +import { deepEqual } from "./utils"; export type useDataConnectQueryOptions< TData = object, @@ -48,7 +47,7 @@ export function useDataConnectQuery( const newRef = getRef(refOrResult); if ( newRef.name !== oldRef.name || - !deepEqual(oldRef, newRef, { strict: true }) + !deepEqual(oldRef.variables, newRef.variables) ) { return newRef; } diff --git a/packages/react/src/data-connect/utils.test.ts b/packages/react/src/data-connect/utils.test.ts new file mode 100644 index 0000000..5cb0fd9 --- /dev/null +++ b/packages/react/src/data-connect/utils.test.ts @@ -0,0 +1,42 @@ +import { deepEqual } from "./useDataConnectQuery"; +import { beforeEach, describe, expect, test } from "vitest"; + +describe('utils', () => { + test('should compare native types correctly', () => { + const nonEqualNumbers = deepEqual(1, 2); + expect(nonEqualNumbers).to.eq(false); + const equalNumbers = deepEqual(2, 2); + expect(equalNumbers).to.eq(true); + const nonEqualBools = deepEqual(false, true); + expect(nonEqualBools).to.eq(false); + const equalBools = deepEqual(false, false); + expect(equalBools).to.eq(true); + const nonEqualStrings = deepEqual('a', 'b'); + expect(nonEqualStrings).to.eq(false); + const equalStrings = deepEqual('a', 'a'); + expect(equalStrings).to.eq(true); + }); + test('should compare object types correctly', () => { + const equalNulls = deepEqual(null, null); + expect(equalNulls).to.eq(true); + const nonEqualNulls = deepEqual(null, {}); + expect(nonEqualNulls).to.eq(false); + const nonEqualObjects = deepEqual({}, {a: 'b'}); + expect(nonEqualObjects).to.eq(false); + const equalObjects = deepEqual({a: 'b'}, {a: 'b'}); + expect(equalObjects).to.eq(true); + const equalObjectsWtesthOrder = deepEqual({ a: 'b', b: 'c'}, {b: 'c', a: 'b'}); + expect(equalObjectsWtesthOrder).to.eq(true); + const nestedObjects = deepEqual({ a: { movie_insert: 'b'}}, {a: { movie_insert: 'c' }}); + expect(nestedObjects).to.eq(false); + + }); +test('should compare arrays correctly', () => { + const emptyArrays = deepEqual([], []); + expect(emptyArrays).to.eq(true); + const nonEmptyArrays = deepEqual([1], [1]); + expect(nonEmptyArrays).to.eq(true); + const nonEmptyDiffArrays = deepEqual([2], [1]); + expect(nonEmptyDiffArrays).to.eq(false); + }); +}); \ No newline at end of file diff --git a/packages/react/src/data-connect/utils.ts b/packages/react/src/data-connect/utils.ts new file mode 100644 index 0000000..20ca9ef --- /dev/null +++ b/packages/react/src/data-connect/utils.ts @@ -0,0 +1,33 @@ +export function deepEqual(a: unknown, b: unknown) { + if(typeof a !== typeof b) { + return false; + } + if(typeof a === 'object' && a !== null) { + if(Array.isArray(a)) { + if(a.length !== (b as unknown[]).length) { + return false; + } + for (let index = 0; index < a.length; index++) { + const elementA = a[index]; + const elementB = (b as unknown[])[index]; + const isEqual = deepEqual(elementA, elementB); + if(!isEqual) { + return false; + } + } + return true; + } + const keys = Object.keys(a); + if(keys.length !== Object.keys(b as object).length) { + return false; + } + for(const key of keys) { + const isEqual = deepEqual(a[key as keyof typeof a], (b as object)[key as keyof typeof b]); + if(!isEqual) { + return false; + } + } + return true; + } + return a === b; +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6596518..f74bb3a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -152,12 +152,6 @@ importers: '@tanstack/react-query': specifier: ^5 version: 5.66.9(react@19.0.0) - '@types/deep-equal': - specifier: ^1.0.4 - version: 1.0.4 - deep-equal: - specifier: ^2.2.3 - version: 2.2.3 firebase: specifier: ^11.3.0 version: 11.3.1 @@ -1364,9 +1358,6 @@ packages: '@types/aria-query@5.0.4': resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} - '@types/deep-equal@1.0.4': - resolution: {integrity: sha512-tqdiS4otQP4KmY0PR3u6KbZ5EWvhNdUoS/jc93UuK23C220lOZ/9TvjfxdPcKvqwwDVtmtSCrnr0p/2dirAxkA==} - '@types/estree@1.0.6': resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} @@ -1785,10 +1776,6 @@ packages: resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} engines: {node: '>=6'} - deep-equal@2.2.3: - resolution: {integrity: sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==} - engines: {node: '>= 0.4'} - deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -1863,9 +1850,6 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-get-iterator@1.1.3: - resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==} - es-iterator-helpers@1.2.1: resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==} engines: {node: '>= 0.4'} @@ -2229,10 +2213,6 @@ packages: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} - is-arguments@1.2.0: - resolution: {integrity: sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==} - engines: {node: '>= 0.4'} - is-array-buffer@3.0.5: resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} engines: {node: '>= 0.4'} @@ -2624,10 +2604,6 @@ packages: resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} engines: {node: '>= 0.4'} - object-is@1.1.6: - resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==} - engines: {node: '>= 0.4'} - object-keys@1.1.1: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} @@ -3011,10 +2987,6 @@ packages: std-env@3.8.0: resolution: {integrity: sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==} - stop-iteration-iterator@1.1.0: - resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} - engines: {node: '>= 0.4'} - streamsearch@1.1.0: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} engines: {node: '>=10.0.0'} @@ -4464,8 +4436,6 @@ snapshots: '@types/aria-query@5.0.4': {} - '@types/deep-equal@1.0.4': {} - '@types/estree@1.0.6': {} '@types/json-schema@7.0.15': {} @@ -4945,27 +4915,6 @@ snapshots: deep-eql@5.0.2: {} - deep-equal@2.2.3: - dependencies: - array-buffer-byte-length: 1.0.2 - call-bind: 1.0.8 - es-get-iterator: 1.1.3 - get-intrinsic: 1.3.0 - is-arguments: 1.2.0 - is-array-buffer: 3.0.5 - is-date-object: 1.1.0 - is-regex: 1.2.1 - is-shared-array-buffer: 1.0.4 - isarray: 2.0.5 - object-is: 1.1.6 - object-keys: 1.1.1 - object.assign: 4.1.7 - regexp.prototype.flags: 1.5.4 - side-channel: 1.1.0 - which-boxed-primitive: 1.1.1 - which-collection: 1.0.2 - which-typed-array: 1.1.18 - deep-is@0.1.4: {} defaults@1.0.4: @@ -5082,18 +5031,6 @@ snapshots: es-errors@1.3.0: {} - es-get-iterator@1.1.3: - dependencies: - call-bind: 1.0.8 - get-intrinsic: 1.3.0 - has-symbols: 1.1.0 - is-arguments: 1.2.0 - is-map: 2.0.3 - is-set: 2.0.3 - is-string: 1.1.1 - isarray: 2.0.5 - stop-iteration-iterator: 1.1.0 - es-iterator-helpers@1.2.1: dependencies: call-bind: 1.0.8 @@ -5627,11 +5564,6 @@ snapshots: hasown: 2.0.2 side-channel: 1.1.0 - is-arguments@1.2.0: - dependencies: - call-bound: 1.0.3 - has-tostringtag: 1.0.2 - is-array-buffer@3.0.5: dependencies: call-bind: 1.0.8 @@ -6015,11 +5947,6 @@ snapshots: object-inspect@1.13.4: {} - object-is@1.1.6: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - object-keys@1.1.1: {} object.assign@4.1.7: @@ -6466,11 +6393,6 @@ snapshots: std-env@3.8.0: {} - stop-iteration-iterator@1.1.0: - dependencies: - es-errors: 1.3.0 - internal-slot: 1.1.0 - streamsearch@1.1.0: {} string-width@4.2.3: From 2e2e51603d409da09f93d1c060b5360e4b682e84 Mon Sep 17 00:00:00 2001 From: Maneesh Tewani Date: Fri, 28 Mar 2025 13:47:15 -0700 Subject: [PATCH 3/6] Fixed formatting --- .../src/data-connect/useDataConnectQuery.ts | 5 +- packages/react/src/data-connect/utils.test.ts | 85 ++++++++++--------- packages/react/src/data-connect/utils.ts | 23 ++--- 3 files changed, 59 insertions(+), 54 deletions(-) diff --git a/packages/react/src/data-connect/useDataConnectQuery.ts b/packages/react/src/data-connect/useDataConnectQuery.ts index 60fbb9f..3270305 100644 --- a/packages/react/src/data-connect/useDataConnectQuery.ts +++ b/packages/react/src/data-connect/useDataConnectQuery.ts @@ -1,7 +1,4 @@ -import { - type UseQueryOptions, - useQuery, -} from "@tanstack/react-query"; +import { type UseQueryOptions, useQuery } from "@tanstack/react-query"; import type { FirebaseError } from "firebase/app"; import { type CallerSdkType, diff --git a/packages/react/src/data-connect/utils.test.ts b/packages/react/src/data-connect/utils.test.ts index 5cb0fd9..6d38e11 100644 --- a/packages/react/src/data-connect/utils.test.ts +++ b/packages/react/src/data-connect/utils.test.ts @@ -1,42 +1,47 @@ -import { deepEqual } from "./useDataConnectQuery"; import { beforeEach, describe, expect, test } from "vitest"; +import { deepEqual } from "./useDataConnectQuery"; -describe('utils', () => { - test('should compare native types correctly', () => { - const nonEqualNumbers = deepEqual(1, 2); - expect(nonEqualNumbers).to.eq(false); - const equalNumbers = deepEqual(2, 2); - expect(equalNumbers).to.eq(true); - const nonEqualBools = deepEqual(false, true); - expect(nonEqualBools).to.eq(false); - const equalBools = deepEqual(false, false); - expect(equalBools).to.eq(true); - const nonEqualStrings = deepEqual('a', 'b'); - expect(nonEqualStrings).to.eq(false); - const equalStrings = deepEqual('a', 'a'); - expect(equalStrings).to.eq(true); - }); - test('should compare object types correctly', () => { - const equalNulls = deepEqual(null, null); - expect(equalNulls).to.eq(true); - const nonEqualNulls = deepEqual(null, {}); - expect(nonEqualNulls).to.eq(false); - const nonEqualObjects = deepEqual({}, {a: 'b'}); - expect(nonEqualObjects).to.eq(false); - const equalObjects = deepEqual({a: 'b'}, {a: 'b'}); - expect(equalObjects).to.eq(true); - const equalObjectsWtesthOrder = deepEqual({ a: 'b', b: 'c'}, {b: 'c', a: 'b'}); - expect(equalObjectsWtesthOrder).to.eq(true); - const nestedObjects = deepEqual({ a: { movie_insert: 'b'}}, {a: { movie_insert: 'c' }}); - expect(nestedObjects).to.eq(false); - - }); -test('should compare arrays correctly', () => { - const emptyArrays = deepEqual([], []); - expect(emptyArrays).to.eq(true); - const nonEmptyArrays = deepEqual([1], [1]); - expect(nonEmptyArrays).to.eq(true); - const nonEmptyDiffArrays = deepEqual([2], [1]); - expect(nonEmptyDiffArrays).to.eq(false); - }); -}); \ No newline at end of file +describe("utils", () => { + test("should compare native types correctly", () => { + const nonEqualNumbers = deepEqual(1, 2); + expect(nonEqualNumbers).to.eq(false); + const equalNumbers = deepEqual(2, 2); + expect(equalNumbers).to.eq(true); + const nonEqualBools = deepEqual(false, true); + expect(nonEqualBools).to.eq(false); + const equalBools = deepEqual(false, false); + expect(equalBools).to.eq(true); + const nonEqualStrings = deepEqual("a", "b"); + expect(nonEqualStrings).to.eq(false); + const equalStrings = deepEqual("a", "a"); + expect(equalStrings).to.eq(true); + }); + test("should compare object types correctly", () => { + const equalNulls = deepEqual(null, null); + expect(equalNulls).to.eq(true); + const nonEqualNulls = deepEqual(null, {}); + expect(nonEqualNulls).to.eq(false); + const nonEqualObjects = deepEqual({}, { a: "b" }); + expect(nonEqualObjects).to.eq(false); + const equalObjects = deepEqual({ a: "b" }, { a: "b" }); + expect(equalObjects).to.eq(true); + const equalObjectsWtesthOrder = deepEqual( + { a: "b", b: "c" }, + { b: "c", a: "b" }, + ); + expect(equalObjectsWtesthOrder).to.eq(true); + const nestedObjects = deepEqual( + { a: { movie_insert: "b" } }, + { a: { movie_insert: "c" } }, + ); + expect(nestedObjects).to.eq(false); + }); + test("should compare arrays correctly", () => { + const emptyArrays = deepEqual([], []); + expect(emptyArrays).to.eq(true); + const nonEmptyArrays = deepEqual([1], [1]); + expect(nonEmptyArrays).to.eq(true); + const nonEmptyDiffArrays = deepEqual([2], [1]); + expect(nonEmptyDiffArrays).to.eq(false); + }); +}); diff --git a/packages/react/src/data-connect/utils.ts b/packages/react/src/data-connect/utils.ts index 20ca9ef..100705d 100644 --- a/packages/react/src/data-connect/utils.ts +++ b/packages/react/src/data-connect/utils.ts @@ -1,33 +1,36 @@ export function deepEqual(a: unknown, b: unknown) { - if(typeof a !== typeof b) { + if (typeof a !== typeof b) { return false; } - if(typeof a === 'object' && a !== null) { - if(Array.isArray(a)) { - if(a.length !== (b as unknown[]).length) { + if (typeof a === "object" && a !== null) { + if (Array.isArray(a)) { + if (a.length !== (b as unknown[]).length) { return false; } for (let index = 0; index < a.length; index++) { const elementA = a[index]; const elementB = (b as unknown[])[index]; const isEqual = deepEqual(elementA, elementB); - if(!isEqual) { + if (!isEqual) { return false; } } return true; } const keys = Object.keys(a); - if(keys.length !== Object.keys(b as object).length) { + if (keys.length !== Object.keys(b as object).length) { return false; } - for(const key of keys) { - const isEqual = deepEqual(a[key as keyof typeof a], (b as object)[key as keyof typeof b]); - if(!isEqual) { + for (const key of keys) { + const isEqual = deepEqual( + a[key as keyof typeof a], + (b as object)[key as keyof typeof b], + ); + if (!isEqual) { return false; } } return true; } return a === b; -} \ No newline at end of file +} From 0204b6e88633538867940c5f184faab130f97b34 Mon Sep 17 00:00:00 2001 From: Maneesh Tewani Date: Fri, 28 Mar 2025 13:50:13 -0700 Subject: [PATCH 4/6] Fixed imports --- packages/react/src/data-connect/utils.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react/src/data-connect/utils.test.ts b/packages/react/src/data-connect/utils.test.ts index 6d38e11..959fd81 100644 --- a/packages/react/src/data-connect/utils.test.ts +++ b/packages/react/src/data-connect/utils.test.ts @@ -1,5 +1,5 @@ -import { beforeEach, describe, expect, test } from "vitest"; -import { deepEqual } from "./useDataConnectQuery"; +import { describe, expect, test } from "vitest"; +import { deepEqual } from "./utils"; describe("utils", () => { test("should compare native types correctly", () => { From 519b2bb207eab65a529df59242b395601e633356 Mon Sep 17 00:00:00 2001 From: Maneesh Tewani Date: Fri, 28 Mar 2025 14:28:43 -0700 Subject: [PATCH 5/6] Added case for when both vars are equivalent --- packages/react/src/data-connect/utils.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/react/src/data-connect/utils.ts b/packages/react/src/data-connect/utils.ts index 100705d..e388187 100644 --- a/packages/react/src/data-connect/utils.ts +++ b/packages/react/src/data-connect/utils.ts @@ -3,6 +3,9 @@ export function deepEqual(a: unknown, b: unknown) { return false; } if (typeof a === "object" && a !== null) { + if(a === b) { + return true; + } if (Array.isArray(a)) { if (a.length !== (b as unknown[]).length) { return false; From d147b17996ca32167609f631a3182e98bfc9ad35 Mon Sep 17 00:00:00 2001 From: Maneesh Tewani Date: Fri, 28 Mar 2025 14:28:54 -0700 Subject: [PATCH 6/6] Fixed file --- packages/react/src/data-connect/utils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react/src/data-connect/utils.ts b/packages/react/src/data-connect/utils.ts index e388187..c199548 100644 --- a/packages/react/src/data-connect/utils.ts +++ b/packages/react/src/data-connect/utils.ts @@ -3,8 +3,8 @@ export function deepEqual(a: unknown, b: unknown) { return false; } if (typeof a === "object" && a !== null) { - if(a === b) { - return true; + if (a === b) { + return true; } if (Array.isArray(a)) { if (a.length !== (b as unknown[]).length) {