Skip to content

Commit 813962f

Browse files
committed
Merge remote-tracking branch 'origin/main' into v5
# Conflicts: # packages/eslint-plugin-query/src/rules/prefer-query-object-syntax/prefer-query-object-syntax.test.ts # packages/eslint-plugin-query/src/rules/prefer-query-object-syntax/prefer-query-object-syntax.ts # packages/query-core/src/mutationCache.ts # packages/query-core/src/queryCache.ts # pnpm-lock.yaml
2 parents c78a8ed + b32da31 commit 813962f

File tree

25 files changed

+942
-112
lines changed

25 files changed

+942
-112
lines changed

docs/config.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,10 @@
281281
{
282282
"label": "Offline Queries and Mutations",
283283
"to": "react/examples/react/offline"
284+
},
285+
{
286+
"label": "Algolia",
287+
"to": "react/examples/react/algolia"
284288
}
285289
]
286290
},

docs/react/guides/ssr.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ async function handleRequest(req, res) {
286286
```tsx
287287
import { HydrationBoundary, QueryClient, QueryClientProvider } from '@tanstack/react-query'
288288

289-
const dehydratedState = window.__REACT_QUERY_STATE__
289+
const { dehydratedState } = window.__REACT_QUERY_STATE__
290290

291291
const queryClient = new QueryClient()
292292

examples/react/algolia/.eslintrc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"extends": ["react-app", "prettier", "plugin:@tanstack/eslint-plugin-query/recommended"],
3+
"rules": {
4+
"react/jsx-uses-react": "off",
5+
"react/react-in-jsx-scope": "off"
6+
}
7+
}

examples/react/algolia/.gitignore

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.js
7+
8+
# testing
9+
/coverage
10+
11+
# production
12+
/build
13+
14+
pnpm-lock.yaml
15+
yarn.lock
16+
package-lock.json
17+
18+
# misc
19+
.DS_Store
20+
.env.local
21+
.env.development.local
22+
.env.test.local
23+
.env.production.local
24+
25+
npm-debug.log*
26+
yarn-debug.log*
27+
yarn-error.log*

examples/react/algolia/.prettierrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{}

examples/react/algolia/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Example
2+
3+
To run this example:
4+
5+
- `npm install`
6+
- `npm run dev`

examples/react/algolia/index.html

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<link rel="shortcut icon" type="image/svg+xml" href="/emblem-light.svg" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1" />
7+
<meta name="theme-color" content="#000000" />
8+
9+
<title>TanStack Query React Algolia example App</title>
10+
</head>
11+
<body>
12+
<noscript>You need to enable JavaScript to run this app.</noscript>
13+
<div id="root"></div>
14+
<script type="module" src="/src/index.tsx"></script>
15+
</body>
16+
</html>

examples/react/algolia/package.json

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
"name": "@tanstack/query-example-react-algolia",
3+
"version": "0.0.1",
4+
"main": "src/index.tsx",
5+
"scripts": {
6+
"dev": "vite",
7+
"build": "tsc && vite build",
8+
"preview": "vite preview"
9+
},
10+
"dependencies": {
11+
"@tanstack/react-query": "^4.7.1",
12+
"@tanstack/react-query-devtools": "^4.7.1",
13+
"@algolia/client-search": "4.11.0",
14+
"@algolia/transporter": "4.11.0",
15+
"algoliasearch": "4.12.2"
16+
},
17+
"devDependencies": {
18+
"@tanstack/eslint-plugin-query": "^4.13.0",
19+
"@vitejs/plugin-react": "^2.0.0",
20+
"vite": "^3.0.0",
21+
"react": "^18.2.0",
22+
"react-dom": "^18.2.0",
23+
"typescript": "^4.7.4",
24+
"@types/react": "^18.0.14",
25+
"@types/react-dom": "^18.0.5"
26+
},
27+
"browserslist": {
28+
"production": [
29+
">0.2%",
30+
"not dead",
31+
"not op_mini all"
32+
],
33+
"development": [
34+
"last 1 chrome version",
35+
"last 1 firefox version",
36+
"last 1 safari version"
37+
]
38+
}
39+
}

examples/react/algolia/src/App.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
2+
3+
import "./styles.css";
4+
import Search from "./Search";
5+
6+
const queryClient = new QueryClient();
7+
8+
export default function App() {
9+
return (
10+
<QueryClientProvider client={queryClient}>
11+
<div className="App">
12+
<h1>TanStack Query with Algolia</h1>
13+
<Search />
14+
</div>
15+
</QueryClientProvider>
16+
);
17+
}

examples/react/algolia/src/Search.tsx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { useState } from "react";
2+
3+
import SearchResults from "./SearchResults";
4+
5+
export default function Search() {
6+
const [query, setQuery] = useState("");
7+
8+
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>) => {
9+
event.preventDefault();
10+
// It is recommended to debounce this event in prod
11+
setQuery(event.target.value);
12+
};
13+
14+
return (
15+
<div>
16+
<input
17+
onChange={handleOnChange}
18+
value={query}
19+
placeholder="Search products"
20+
/>
21+
<SearchResults query={query} />
22+
</div>
23+
);
24+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import useAlgolia from "./useAlgolia";
2+
3+
type Product = {
4+
name: string;
5+
shortDescription: string;
6+
salePrice: number;
7+
};
8+
9+
type SearchResultsProps = {
10+
query: string;
11+
};
12+
13+
export default function SearchResults({ query = "" }: SearchResultsProps) {
14+
const {
15+
hits,
16+
isLoading,
17+
isFetching,
18+
status,
19+
hasNextPage,
20+
isFetchingNextPage,
21+
fetchNextPage,
22+
} = useAlgolia<Product>({
23+
indexName: "bestbuy",
24+
query,
25+
hitsPerPage: 5,
26+
staleTime: 1000 * 30, // 30s
27+
cacheTime: 1000 * 60 * 15, // 15m
28+
enabled: !!query,
29+
});
30+
31+
if (!query) return null;
32+
33+
if (isLoading) return <div className="loading">Loading...</div>;
34+
35+
return (
36+
<div>
37+
<div className="search-status">
38+
Satus: {status} {isFetching && <span>fetching...</span>}
39+
</div>
40+
<div>
41+
<div className="search-result">
42+
{hits && hits.length > 0 ? (
43+
hits.map((product) => (
44+
<li key={product.objectID} className="product">
45+
<span className="product-name">{product.name}</span>
46+
{product.shortDescription && (
47+
<>
48+
<br />
49+
<span className="product-description">
50+
{product.shortDescription}
51+
</span>
52+
</>
53+
)}
54+
<br />
55+
<span className="product-price">${product.salePrice}</span>
56+
</li>
57+
))
58+
) : (
59+
<h3>No products found!</h3>
60+
)}
61+
</div>
62+
{hasNextPage && (
63+
<div className="search-more" onClick={() => fetchNextPage()}>
64+
more
65+
</div>
66+
)}
67+
{isFetchingNextPage && (
68+
<div className="search-status">Fetching next page...</div>
69+
)}
70+
</div>
71+
</div>
72+
);
73+
}

examples/react/algolia/src/algolia.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import algoliasearch from "algoliasearch";
2+
import { Hit } from "@algolia/client-search";
3+
4+
// From Algolia example
5+
// https://github.com/algolia/react-instantsearch
6+
const ALGOLIA_APP_ID = "latency";
7+
const ALGOLIA_SEARCH_API_KEY = "6be0576ff61c053d5f9a3225e2a90f76";
8+
9+
type SearchOptions = {
10+
indexName: string;
11+
query: string;
12+
pageParam: number;
13+
hitsPerPage: number;
14+
};
15+
16+
export async function search<TData>({
17+
indexName,
18+
query,
19+
pageParam = 0,
20+
hitsPerPage = 10,
21+
}: SearchOptions): Promise<{
22+
hits: Hit<TData>[];
23+
nextPage: number | undefined;
24+
}> {
25+
const client = algoliasearch(ALGOLIA_APP_ID, ALGOLIA_SEARCH_API_KEY);
26+
const index = client.initIndex(indexName);
27+
28+
console.log("alogolia:search", { indexName, query, pageParam, hitsPerPage });
29+
30+
const { hits, page, nbPages } = await index.search<TData>(query, {
31+
page: pageParam,
32+
hitsPerPage,
33+
});
34+
35+
const nextPage = page + 1 < nbPages ? page + 1 : undefined;
36+
37+
return { hits, nextPage };
38+
}

examples/react/algolia/src/index.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import ReactDOM from "react-dom/client";
2+
3+
import App from "./App";
4+
5+
const rootElement = document.getElementById("root") as HTMLElement;
6+
ReactDOM.createRoot(rootElement).render(<App />);

examples/react/algolia/src/styles.css

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
.App {
2+
font-family: sans-serif;
3+
font-size: 14px;
4+
padding: 20px;
5+
}
6+
7+
.loading {
8+
padding-top: 20px;
9+
padding-left: 20px;
10+
color: darkmagenta;
11+
}
12+
13+
.search-status {
14+
padding-top: 20px;
15+
padding-left: 20px;
16+
color: gray;
17+
}
18+
19+
.search-result {
20+
padding-left: 20px;
21+
padding-top: 20px;
22+
}
23+
24+
.search-more {
25+
color: blue;
26+
padding-left: 20px;
27+
padding-top: 20px;
28+
cursor: pointer;
29+
font-weight: bold;
30+
text-transform: uppercase;
31+
}
32+
33+
.product {
34+
padding-bottom: 5px;
35+
}
36+
37+
.product-name {
38+
font-weight: bold;
39+
font-size: 12px;
40+
}
41+
42+
.product-description {
43+
font-size: 12px;
44+
}
45+
46+
.product-price {
47+
font-size: 12px;
48+
font-weight: bold;
49+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { useInfiniteQuery } from "@tanstack/react-query";
2+
import { search } from "./algolia";
3+
4+
export type UseAlgoliaOptions = {
5+
indexName: string;
6+
query: string;
7+
hitsPerPage?: number;
8+
staleTime?: number;
9+
cacheTime?: number;
10+
enabled?: boolean;
11+
};
12+
13+
export default function useAlgolia<TData>({
14+
indexName,
15+
query,
16+
hitsPerPage = 10,
17+
staleTime,
18+
cacheTime,
19+
enabled,
20+
}: UseAlgoliaOptions) {
21+
const queryInfo = useInfiniteQuery({
22+
queryKey: ["algolia", indexName, query, hitsPerPage],
23+
queryFn: ({ pageParam }) =>
24+
search<TData>({ indexName, query, pageParam, hitsPerPage }),
25+
getNextPageParam: (lastPage) => lastPage?.nextPage,
26+
staleTime,
27+
cacheTime,
28+
enabled,
29+
});
30+
31+
const hits = queryInfo.data?.pages.map((page) => page.hits).flat();
32+
33+
return { ...queryInfo, hits };
34+
}

examples/react/algolia/tsconfig.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"compilerOptions": {
3+
"target": "ES2020",
4+
"lib": [
5+
"dom",
6+
"dom.iterable",
7+
"esnext"
8+
],
9+
"allowJs": true,
10+
"skipLibCheck": true,
11+
"esModuleInterop": true,
12+
"allowSyntheticDefaultImports": true,
13+
"strict": true,
14+
"forceConsistentCasingInFileNames": true,
15+
"noFallthroughCasesInSwitch": true,
16+
"module": "esnext",
17+
"moduleResolution": "node",
18+
"resolveJsonModule": true,
19+
"isolatedModules": true,
20+
"noEmit": true,
21+
"jsx": "react-jsx"
22+
},
23+
"include": [
24+
"src"
25+
]
26+
}

0 commit comments

Comments
 (0)