this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

debounce

+76 -33
+1
package.json
··· 17 17 "@t3-oss/env-core": "^0.13.4", 18 18 "@tailwindcss/vite": "^4.1.7", 19 19 "@tanstack/pacer": "^0.7.0", 20 + "@tanstack/react-pacer": "^0.7.0", 20 21 "@tanstack/react-query": "^5.76.2", 21 22 "@tanstack/react-query-devtools": "^5.76.2", 22 23 "@tanstack/react-router": "1.121.0-alpha.11",
+16
pnpm-lock.yaml
··· 26 26 '@tanstack/pacer': 27 27 specifier: ^0.7.0 28 28 version: 0.7.0 29 + '@tanstack/react-pacer': 30 + specifier: ^0.7.0 31 + version: 0.7.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) 29 32 '@tanstack/react-query': 30 33 specifier: ^5.76.2 31 34 version: 5.76.2(react@19.1.0) ··· 1239 1242 1240 1243 '@tanstack/query-devtools@5.76.0': 1241 1244 resolution: {integrity: sha512-1p92nqOBPYVqVDU0Ua5nzHenC6EGZNrLnB2OZphYw8CNA1exuvI97FVgIKON7Uug3uQqvH/QY8suUKpQo8qHNQ==} 1245 + 1246 + '@tanstack/react-pacer@0.7.0': 1247 + resolution: {integrity: sha512-1QymYEkRnkcDxARJBb6pCMRtflIqMMsZExa27Qk9npl/StY3fQZZLH2wrNuzi1dyeMdqIxFjx8fm/LqZcxPP3Q==} 1248 + engines: {node: '>=18'} 1249 + peerDependencies: 1250 + react: '>=16.8' 1251 + react-dom: '>=16.8' 1242 1252 1243 1253 '@tanstack/react-query-devtools@5.76.2': 1244 1254 resolution: {integrity: sha512-XQqPM7ByqhitYtzzRXpFcPXY2FIMGY13atJoQnDInxX+ko7wOhAJg51QGzSxQwZ2JEy91taj0IaIUh3pOz2buw==} ··· 5023 5033 '@tanstack/query-core@5.76.2': {} 5024 5034 5025 5035 '@tanstack/query-devtools@5.76.0': {} 5036 + 5037 + '@tanstack/react-pacer@0.7.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': 5038 + dependencies: 5039 + '@tanstack/pacer': 0.7.0 5040 + react: 19.1.0 5041 + react-dom: 19.1.0(react@19.1.0) 5026 5042 5027 5043 '@tanstack/react-query-devtools@5.76.2(@tanstack/react-query@5.76.2(react@19.1.0))(react@19.1.0)': 5028 5044 dependencies:
+59 -33
src/routes/debounced-preload-filters.tsx
··· 1 + import { useDebouncedCallback } from "@tanstack/react-pacer"; 1 2 import { 2 3 queryOptions, 3 4 useQueryClient, ··· 28 29 name: v.optional(v.string(), ""), 29 30 }); 30 31 31 - function FilterSubmitContextProvider(props: { 32 - initialName: string; 33 - handleSubmit: (nameFilter: string) => void; 34 - children: React.ReactNode; 35 - }) { 36 - const [nameFilter, setNameFilter] = useState(props.initialName); 37 - 38 - const handleSubmit = useCallback(() => { 39 - props.handleSubmit(nameFilter); 40 - }, [nameFilter, props]); 41 - 42 - return ( 43 - <FilterSubmitContext.Provider 44 - value={{ handleSubmit, nameFilter, updateNameFilter: setNameFilter }} 45 - > 46 - {props.children} 47 - </FilterSubmitContext.Provider> 48 - ); 49 - } 50 - 51 32 export const Route = createFileRoute({ 52 33 validateSearch: searchParamsSchema, 53 34 loaderDeps: ({ search }) => ({ ··· 76 57 component: RouteComponent, 77 58 }); 78 59 60 + function PreloadFilterSubmitContextProvider(props: { 61 + initialName: string; 62 + handleSubmit: (nameFilter: string) => void; 63 + children: React.ReactNode; 64 + }) { 65 + const queryClient = useQueryClient(); 66 + const { pokemonListOptions: serverPokemonListOptions } = 67 + Route.useRouteContext(); 68 + const [nameFilter, setNameFilter] = useState(props.initialName); 69 + const getFilteredPokemonListQueryFn = useServerFn( 70 + getServerFilteredPokemonListQueryFn, 71 + ); 72 + 73 + const debouncedNameFilter = useDebouncedCallback( 74 + (newNameFilter: string) => { 75 + void queryClient.prefetchQuery({ 76 + queryKey: getFilteredPokemonListQueryKey( 77 + serverPokemonListOptions.queryKey[1], 78 + serverPokemonListOptions.queryKey[2].offset, 79 + newNameFilter, 80 + ), 81 + queryFn: getFilteredPokemonListQueryFn, 82 + }); 83 + }, 84 + { 85 + wait: 100, 86 + }, 87 + ); 88 + 89 + const updateNameFilter = useCallback( 90 + (value: string) => { 91 + debouncedNameFilter(value); 92 + setNameFilter(value); 93 + }, 94 + [debouncedNameFilter], 95 + ); 96 + 97 + const handleSubmit = useCallback(() => { 98 + props.handleSubmit(nameFilter); 99 + }, [nameFilter, props]); 100 + 101 + return ( 102 + <FilterSubmitContext.Provider 103 + value={{ handleSubmit, nameFilter, updateNameFilter }} 104 + > 105 + {props.children} 106 + </FilterSubmitContext.Provider> 107 + ); 108 + } 109 + 79 110 function RouteComponent() { 80 111 const { offset: currentOffset, name: nameFilter } = Route.useSearch(); 81 112 const navigate = Route.useNavigate(); 82 113 const { pokemonListOptions: serverPokemonListOptions } = 83 114 Route.useRouteContext(); 84 115 const queryClient = useQueryClient(); 116 + const getFilteredPokemonListQueryFn = useServerFn( 117 + getServerFilteredPokemonListQueryFn, 118 + ); 85 119 86 120 const { data } = useSuspenseQuery({ 87 121 ...serverPokemonListOptions, 88 - queryFn: useServerFn(getServerFilteredPokemonListQueryFn), 122 + queryFn: getFilteredPokemonListQueryFn, 89 123 }); 90 124 91 125 if (data.prevOffset !== null) { 92 126 void queryClient.prefetchQuery({ 93 127 ...serverPokemonListOptions, 94 - queryKey: getFilteredPokemonListQueryKey( 95 - "filters", 96 - data.prevOffset, 97 - nameFilter, 98 - ), 128 + queryFn: getFilteredPokemonListQueryFn, 99 129 }); 100 130 } 101 131 102 132 if (data.nextOffset !== null) { 103 133 void queryClient.prefetchQuery({ 104 134 ...serverPokemonListOptions, 105 - queryKey: getFilteredPokemonListQueryKey( 106 - "filters", 107 - data.nextOffset, 108 - nameFilter, 109 - ), 135 + queryFn: getFilteredPokemonListQueryFn, 110 136 }); 111 137 } 112 138 ··· 123 149 {/* Filter UI */} 124 150 <div className="mb-6 p-4 border rounded-lg bg-gray-50"> 125 151 <h2 className="text-lg font-semibold mb-3">Filters</h2> 126 - <FilterSubmitContextProvider 152 + <PreloadFilterSubmitContextProvider 127 153 initialName={nameFilter} 128 154 handleSubmit={(nameFilter) => { 129 155 navigate({ ··· 132 158 }} 133 159 > 134 160 <FilterForm /> 135 - </FilterSubmitContextProvider> 161 + </PreloadFilterSubmitContextProvider> 136 162 </div> 137 163 138 164 <Table>