diff --git a/.changeset/spotty-jokes-swim.md b/.changeset/spotty-jokes-swim.md new file mode 100644 index 000000000000..184a497f772d --- /dev/null +++ b/.changeset/spotty-jokes-swim.md @@ -0,0 +1,9 @@ +--- +"@refinedev/core": patch +--- + +fix(core): wrap `setFilters` and `setSorters` methods with `useCallback` to prevent looping re-renders + +With this we can use the setFilters as dependencies inside useEffects without infinite loop since state changes in the hook won't cause the functions to be re-assigned + +[Fixes #6385](https://github.com/refinedev/refine/issues/6385) diff --git a/packages/core/src/hooks/useTable/index.ts b/packages/core/src/hooks/useTable/index.ts index 7398aabc13a6..b679e5392468 100644 --- a/packages/core/src/hooks/useTable/index.ts +++ b/packages/core/src/hooks/useTable/index.ts @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect, useCallback } from "react"; import type { QueryObserverResult, @@ -520,40 +520,56 @@ export function useTable< dataProviderName, }); - const setFiltersAsMerge = (newFilters: CrudFilter[]) => { - setFilters((prevFilters) => - unionFilters(preferredPermanentFilters, newFilters, prevFilters), - ); - }; + const setFiltersAsMerge = useCallback( + (newFilters: CrudFilter[]) => { + setFilters((prevFilters) => + unionFilters(preferredPermanentFilters, newFilters, prevFilters), + ); + }, + [preferredPermanentFilters], + ); - const setFiltersAsReplace = (newFilters: CrudFilter[]) => { - setFilters(unionFilters(preferredPermanentFilters, newFilters)); - }; + const setFiltersAsReplace = useCallback( + (newFilters: CrudFilter[]) => { + setFilters(unionFilters(preferredPermanentFilters, newFilters)); + }, + [preferredPermanentFilters], + ); - const setFiltersWithSetter = ( - setter: (prevFilters: CrudFilter[]) => CrudFilter[], - ) => { - setFilters((prev) => unionFilters(preferredPermanentFilters, setter(prev))); - }; + const setFiltersWithSetter = useCallback( + (setter: (prevFilters: CrudFilter[]) => CrudFilter[]) => { + setFilters((prev) => + unionFilters(preferredPermanentFilters, setter(prev)), + ); + }, + [preferredPermanentFilters], + ); - const setFiltersFn: useTableReturnType["setFilters"] = ( - setterOrFilters, - behavior: SetFilterBehavior = prefferedFilterBehavior, - ) => { - if (typeof setterOrFilters === "function") { - setFiltersWithSetter(setterOrFilters); - } else { - if (behavior === "replace") { - setFiltersAsReplace(setterOrFilters); - } else { - setFiltersAsMerge(setterOrFilters); - } - } - }; + const setFiltersFn: useTableReturnType["setFilters"] = + useCallback( + ( + setterOrFilters, + behavior: SetFilterBehavior = prefferedFilterBehavior, + ) => { + if (typeof setterOrFilters === "function") { + setFiltersWithSetter(setterOrFilters); + } else { + if (behavior === "replace") { + setFiltersAsReplace(setterOrFilters); + } else { + setFiltersAsMerge(setterOrFilters); + } + } + }, + [setFiltersWithSetter, setFiltersAsReplace, setFiltersAsMerge], + ); - const setSortWithUnion = (newSorter: CrudSort[]) => { - setSorters(() => unionSorters(preferredPermanentSorters, newSorter)); - }; + const setSortWithUnion = useCallback( + (newSorter: CrudSort[]) => { + setSorters(() => unionSorters(preferredPermanentSorters, newSorter)); + }, + [preferredPermanentSorters], + ); const { elapsedTime } = useLoadingOvertime({ isLoading: queryResult.isFetching,