Skip to content

Commit fb46298

Browse files
authored
fix(react): fix compatibility issues with React 18 (#969)
1 parent 644cc8e commit fb46298

File tree

16 files changed

+376
-10
lines changed

16 files changed

+376
-10
lines changed

.eslintignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ examples/twitter-compose-with-typeahead
88
examples/slack-with-emojis-and-commands
99
examples/react-instantsearch-hooks
1010
examples/vue-instantsearch
11+
examples/react-18

examples/react-18/.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
SKIP_PREFLIGHT_CHECK=true

examples/react-18/.gitignore

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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+
# misc
15+
.DS_Store
16+
.env.local
17+
.env.development.local
18+
.env.test.local
19+
.env.production.local
20+
21+
npm-debug.log*
22+
yarn-debug.log*
23+
yarn-error.log*

examples/react-18/README.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Autocomplete with React 18 example
2+
3+
This example shows how to integrate Autocomplete with [React 18](https://reactjs.org/blog/2022/03/29/react-v18.html).
4+
5+
## Demo
6+
7+
[Access the demo](https://codesandbox.io/s/github/algolia/autocomplete/tree/next/examples/react-18)
8+
9+
## How to run this example locally
10+
11+
### 1. Clone this repository
12+
13+
```sh
14+
git clone [email protected]:algolia/autocomplete.git
15+
```
16+
17+
### 2. Install the dependencies and run the server
18+
19+
```sh
20+
yarn
21+
yarn workspace @algolia/autocomplete-example-react-18 start
22+
```
23+
24+
Alternatively, you may use npm:
25+
26+
```sh
27+
cd examples/react-18
28+
npm install
29+
npm start
30+
```
31+
32+
Open <http://localhost:3000> to see your app.

examples/react-18/package.json

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
{
2+
"name": "@algolia/autocomplete-example-react-18",
3+
"description": "Autocomplete example with React 18",
4+
"version": "1.6.2",
5+
"private": true,
6+
"license": "MIT",
7+
"scripts": {
8+
"start": "react-scripts start",
9+
"build": "react-scripts build"
10+
},
11+
"dependencies": {
12+
"@algolia/autocomplete-js": "1.6.2",
13+
"@algolia/autocomplete-theme-classic": "1.6.2",
14+
"algoliasearch": "4.9.1",
15+
"react": "^18.1.0",
16+
"react-dom": "^18.1.0"
17+
},
18+
"devDependencies": {
19+
"@algolia/client-search": "4.9.1",
20+
"@types/react": "^18.0.0",
21+
"@types/react-dom": "^18.0.0",
22+
"react-scripts": "4.0.3",
23+
"typescript": "^4.4.2"
24+
},
25+
"eslintConfig": {
26+
"extends": [
27+
"react-app"
28+
]
29+
},
30+
"browserslist": {
31+
"production": [
32+
">0.2%",
33+
"not dead",
34+
"not op_mini all"
35+
],
36+
"development": [
37+
"last 1 chrome version",
38+
"last 1 firefox version",
39+
"last 1 safari version"
40+
]
41+
}
42+
}

examples/react-18/public/favicon.png

43.1 KB
Loading

examples/react-18/public/index.html

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<link rel="icon" href="%PUBLIC_URL%/favicon.png" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1" />
7+
<meta name="theme-color" content="#000000" />
8+
<title>React 18 | Autocomplete</title>
9+
</head>
10+
<body>
11+
<noscript>You need to enable JavaScript to run this app.</noscript>
12+
<div id="root"></div>
13+
</body>
14+
</html>

examples/react-18/src/App.tsx

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import { useEffect, useRef, createElement, Fragment } from 'react';
2+
import { createRoot } from 'react-dom/client';
3+
import { autocomplete, getAlgoliaResults } from '@algolia/autocomplete-js';
4+
import algoliasearch from 'algoliasearch/lite';
5+
6+
import type { AutocompleteComponents } from '@algolia/autocomplete-js';
7+
import type { Hit } from '@algolia/client-search';
8+
import type { Root } from 'react-dom/client';
9+
10+
import '@algolia/autocomplete-theme-classic';
11+
12+
const appId = 'latency';
13+
const apiKey = '6be0576ff61c053d5f9a3225e2a90f76';
14+
const searchClient = algoliasearch(appId, apiKey);
15+
16+
type ProductHit = Hit<{
17+
brand: string;
18+
categories: string[];
19+
description: string;
20+
image: string;
21+
name: string;
22+
price: number;
23+
rating: number;
24+
type: string;
25+
url: string;
26+
}>;
27+
28+
export default function App() {
29+
const containerRef = useRef<HTMLDivElement | null>(null);
30+
const panelRootRef = useRef<Root | null>(null);
31+
const rootRef = useRef<HTMLElement | null>(null);
32+
33+
useEffect(() => {
34+
if (!containerRef.current) {
35+
return undefined;
36+
}
37+
38+
const search = autocomplete<ProductHit>({
39+
container: containerRef.current,
40+
placeholder: 'Search',
41+
getSources({ query }) {
42+
return [
43+
{
44+
sourceId: 'products',
45+
getItems() {
46+
return getAlgoliaResults<ProductHit>({
47+
searchClient,
48+
queries: [
49+
{
50+
indexName: 'instant_search',
51+
query,
52+
},
53+
],
54+
});
55+
},
56+
templates: {
57+
item({ item, components }) {
58+
return <ProductItem hit={item} components={components} />;
59+
},
60+
noResults() {
61+
return 'No products matching.';
62+
},
63+
},
64+
},
65+
];
66+
},
67+
renderer: { createElement, Fragment, render: () => {} },
68+
render({ children }, root) {
69+
if (!panelRootRef.current || rootRef.current !== root) {
70+
rootRef.current = root;
71+
72+
panelRootRef.current?.unmount();
73+
panelRootRef.current = createRoot(root);
74+
}
75+
76+
panelRootRef.current.render(children);
77+
},
78+
});
79+
80+
return () => {
81+
search.destroy();
82+
};
83+
}, []);
84+
85+
return <div ref={containerRef} />;
86+
}
87+
88+
type ProductItemProps = {
89+
hit: ProductHit;
90+
components: AutocompleteComponents;
91+
};
92+
93+
function ProductItem({ hit, components }: ProductItemProps) {
94+
return (
95+
<div className="aa-ItemWrapper">
96+
<div className="aa-ItemContent">
97+
<div className="aa-ItemIcon aa-ItemIcon--picture aa-ItemIcon--alignTop">
98+
<img src={hit.image} alt={hit.name} width="40" height="40" />
99+
</div>
100+
101+
<div className="aa-ItemContentBody">
102+
<div className="aa-ItemContentTitle">
103+
<components.Highlight hit={hit} attribute="name" />
104+
</div>
105+
<div className="aa-ItemContentDescription">
106+
By <strong>{hit.brand}</strong> in{' '}
107+
<strong>{hit.categories[0]}</strong>
108+
</div>
109+
</div>
110+
</div>
111+
</div>
112+
);
113+
}

examples/react-18/src/index.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import React from 'react';
2+
import ReactDOM from 'react-dom/client';
3+
import App from './App';
4+
5+
import './styles.css';
6+
7+
const root = ReactDOM.createRoot(
8+
document.getElementById('root') as HTMLElement
9+
);
10+
root.render(
11+
<React.StrictMode>
12+
<App />
13+
</React.StrictMode>
14+
);
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/// <reference types="react-scripts" />

0 commit comments

Comments
 (0)