diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml
index 4131373d..0f75f845 100644
--- a/.github/workflows/node.js.yml
+++ b/.github/workflows/node.js.yml
@@ -49,57 +49,6 @@ jobs:
- run: npm run build --if-present
- run: npm test
- react-16-8:
- runs-on: ubuntu-latest
-
- steps:
- - run: echo "::add-mask::${{ secrets.STADIA_MAPS_API_KEY }}"
- - uses: actions/checkout@v4
- - name: Use Node.js 18.x
- uses: actions/setup-node@v4
- with:
- node-version: 18.x
- - run: |
- npm i react@16.8 react-dom@16.8 react-test-renderer@16.8 @types/react@16 @types/react-dom@16 @testing-library/react@11
- - run: npm i
- - run: tsc
- - run: npm test
- - run: npm run build:examples
-
- react-16-14:
- runs-on: ubuntu-latest
-
- steps:
- - run: echo "::add-mask::${{ secrets.STADIA_MAPS_API_KEY }}"
- - uses: actions/checkout@v4
- - name: Use Node.js 18.x
- uses: actions/setup-node@v4
- with:
- node-version: 18.x
- - run: |
- npm i react@16.14 react-dom@16.14 react-test-renderer@16.14 @types/react@16 @types/react-dom@16 @testing-library/react@11
- - run: npm i
- - run: tsc
- - run: npm test
- - run: npm run build:examples
-
- react-17:
- runs-on: ubuntu-latest
-
- steps:
- - run: echo "::add-mask::${{ secrets.STADIA_MAPS_API_KEY }}"
- - uses: actions/checkout@v4
- - name: Use Node.js 18.x
- uses: actions/setup-node@v4
- with:
- node-version: 18.x
- - run: |
- npm i react@17 react-dom@17 react-test-renderer@17 @types/react@17 @types/react-dom@17 @testing-library/react@11
- - run: npm i
- - run: tsc
- - run: npm test
- - run: npm run build:examples
-
react-18:
runs-on: ubuntu-latest
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0d6727dd..724f48db 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+### [3.0.1] 2024-05-23
+
+- Drop React 16/17, React 18 is now required
+- For OpenLayers 9.2.3
+
# [3.0.0] 2024-05-22
- Do not support multiple OpenLayers versions, link each `rlayers` to one OpenLayers minor version
diff --git a/README.md b/README.md
index bf3d807e..fa4f6aff 100644
--- a/README.md
+++ b/README.md
@@ -65,7 +65,7 @@ _React_ is supported from version 16.8.0.
| 2.1 (_obsolete_) | 6.10, 6.11, 6.12, 6.13, 6.14, 6.14.1, 6.15, 6.15.1, 7.0.0, 7.1.0, 7.2.0, 7.2.2, 7.3.0, 7.4.0, 7.5.1, 8.0.0, 8.1.0 | 16.8, 16.14, 17.0.2, 18.0.0, 18.1.0, 18.2.0 |
| 2.2 (_obsolete_) | 6.10, 6.11, 6.12, 6.13, 6.14, 6.14.1, 6.15, 6.15.1, 7.0.0, 7.1.0, 7.2.0, 7.2.2, 7.3.0, 7.4.0, 7.5.1, 8.0.0, 8.1.0, 8.2.0 | 16.8, 16.14, 17.0.2, 18.0.0, 18.1.0, 18.2.0 |
| 2.3 `(@latest)` | 6.10, 6.11, 6.12, 6.13, 6.14, 6.14.1, 6.15, 6.15.1, 7.0.0, 7.1.0, 7.2.0, 7.2.2, 7.3.0, 7.4.0, 7.5.1, 8.0.0, 8.1.0, 8.2.0, 9.0.0, 9.1.0 | 16.8, 16.14, 17.0.2, 18.0.0, 18.1.0, 18.2.0, 18.3.1 |
-| 3.0 (`@next`) | 9.2.2 | 16.8, 16.14, 17.0.2, 18.0.0, 18.1.0, 18.2.0, 18.3.1 |
+| 3.0 (`@next`) | 9.2.2 | 18.0.0, 18.1.0, 18.2.0, 18.3.1 |
---
diff --git a/examples/index-react18.tsx b/examples/index-react18.tsx
deleted file mode 100644
index 3ba52feb..00000000
--- a/examples/index-react18.tsx
+++ /dev/null
@@ -1,9 +0,0 @@
-import React from 'react';
-import ReactDOMClient from 'react-dom/client';
-
-import debug from 'rlayers/debug';
-
-import App from './App';
-
-debug('React 18 mode');
-ReactDOMClient.createRoot(document.getElementById('root')).render();
diff --git a/examples/index.tsx b/examples/index.tsx
index b443924c..e438453e 100644
--- a/examples/index.tsx
+++ b/examples/index.tsx
@@ -1,9 +1,8 @@
import React from 'react';
-import ReactDOM from 'react-dom';
+import ReactDOMClient from 'react-dom/client';
import debug from 'rlayers/debug';
import App from './App';
-debug('React 16/17 mode');
-ReactDOM.render(, document.getElementById('root'));
+ReactDOMClient.createRoot(document.getElementById('root')).render();
diff --git a/package.json b/package.json
index cd053dbf..df0d86b6 100644
--- a/package.json
+++ b/package.json
@@ -94,8 +94,8 @@
},
"peerDependencies": {
"ol": "9.2.2",
- "react": ">=16.8",
- "react-dom": ">=16.8"
+ "react": ">=18",
+ "react-dom": ">=18"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^25.0.7",
@@ -109,6 +109,7 @@
"@types/proj4": "^2.5.2",
"@types/react": "^18.3.2",
"@types/react-dom": "^18.0.5",
+ "@types/semver": "^7.5.8",
"@typescript-eslint/eslint-plugin": "^7.1.0",
"@typescript-eslint/parser": "^7.1.0",
"@typescript-eslint/typescript-estree": "^7.1.0",
diff --git a/src/style/RStyle.tsx b/src/style/RStyle.tsx
index 502ae47e..2d2c1312 100644
--- a/src/style/RStyle.tsx
+++ b/src/style/RStyle.tsx
@@ -1,5 +1,5 @@
import React, {PropsWithChildren} from 'react';
-import ReactDOM from 'react-dom';
+import {createRoot} from 'react-dom/client';
import {LRUCache} from 'lru-cache';
import {Map, Feature} from 'ol';
import Style, {StyleLike} from 'ol/style/Style';
@@ -71,12 +71,12 @@ export default class RStyle extends React.PureComponent
{this.props.render(f, r)}
);
- ReactDOM.render(render, document.createElement('div'));
+ createRoot(document.createElement('div')).render(reactElement);
if (this.cache) this.cache.set(hash, style);
return style;
};
diff --git a/src/style/RStyleArray.tsx b/src/style/RStyleArray.tsx
index f394303c..845c4d95 100644
--- a/src/style/RStyleArray.tsx
+++ b/src/style/RStyleArray.tsx
@@ -1,5 +1,5 @@
import React from 'react';
-import ReactDOM from 'react-dom';
+import {createRoot} from 'react-dom/client';
import {Feature} from 'ol';
import Style from 'ol/style/Style';
import Geometry from 'ol/geom/Geometry';
@@ -38,12 +38,12 @@ export default class RStyleArray extends RStyle {
throw new TypeError('An RStyleArray should contain only RStyle elements');
});
const styleArray = [];
- const render = (
+ const reactElement = (
{element.props.children}
);
- ReactDOM.render(render, document.createElement('div'));
+ createRoot(document.createElement('div')).render(reactElement);
return styleArray as Style[];
}
return this.ol as Style[];
diff --git a/test/RFeature.test.tsx b/test/RFeature.test.tsx
index e2ce5c6f..53767b5f 100644
--- a/test/RFeature.test.tsx
+++ b/test/RFeature.test.tsx
@@ -1,12 +1,16 @@
-import * as fs from 'fs';
-import React from 'react';
+import React, {act as act_React19} from 'react';
import {fireEvent, render} from '@testing-library/react';
+import {act as act_React_18} from 'react-dom/test-utils';
+import semver from 'semver';
import {Polygon, Point} from 'ol/geom';
import {Feature} from 'ol';
import {RFeature, RLayerVector, RMap, RContext, ROverlay} from 'rlayers';
import * as common from './common';
-import {act} from 'react-dom/test-utils';
+
+const act: (callback: () => T | Promise) => Promise = semver.gte(React.version, '18.3.0')
+ ? act_React19
+ : act_React_18;
describe('', () => {
it('should create features', async () => {
diff --git a/test/RStyle.test.tsx b/test/RStyle.test.tsx
index ae06f500..ee97bf72 100644
--- a/test/RStyle.test.tsx
+++ b/test/RStyle.test.tsx
@@ -1,5 +1,7 @@
-import React from 'react';
+import React, {act as act_React19} from 'react';
import {fireEvent, render} from '@testing-library/react';
+import {act as act_React_18} from 'react-dom/test-utils';
+import semver from 'semver';
import {Feature} from 'ol';
import {Style, Circle, Image, RegularShape} from 'ol/style';
@@ -20,6 +22,10 @@ import {
import {Point} from 'ol/geom';
import * as common from './common';
+const act: (callback: () => T | Promise) => Promise = semver.gte(React.version, '18.3.0')
+ ? act_React19
+ : act_React_18;
+
describe('', () => {
it('should create a basic icon style', async () => {
const ref = createRStyle();
@@ -148,7 +154,9 @@ describe('', () => {
geometry: new Point(common.coords.ArcDeTriomphe),
name: 'text'
});
- const style = (RStyle.getStyle(ref) as (Feature, number) => Style)(f, 100);
+ const style = await act(() => {
+ return (RStyle.getStyle(ref) as (Feature, number) => Style)(f, 100);
+ });
expect(style.getText()?.getText()).toBe('text');
expect(style.getText()?.getFont()).toBe('bold 25px sans-serif');
expect(style.getText()?.getStroke()?.getWidth()).toBe(100);
@@ -229,7 +237,9 @@ describe('', () => {
geometry: new Point(common.coords.ArcDeTriomphe),
name: 'text14'
});
- const style = (RStyle.getStyle(ref) as (Feature) => Style)(f);
+ const style = await act(() => {
+ return (RStyle.getStyle(ref) as (Feature) => Style)(f);
+ });
expect(style.getText()?.getText()).toBe('text14');
expect(style.getText()?.getStroke()?.getWidth()).toBe(14);
expect(ref.current?.cache.get(f.get('name'))).toBe(style);
@@ -267,7 +277,9 @@ describe('', () => {
geometry: new Point(common.coords.ArcDeTriomphe),
name: 'text9'
});
- const style = (ref.current?.ol.getStyle() as (Feature) => Style)(f);
+ const style = await act(() => {
+ return (ref.current?.ol.getStyle() as (Feature) => Style)(f);
+ });
expect(style.getText()?.getText()).toBe('text9');
expect(style.getText()?.getStroke()?.getWidth()).toBe(9);
});
@@ -420,12 +432,19 @@ describe('', () => {
geometry: new Point(common.coords.ArcDeTriomphe),
name: 'text1'
});
- expect(((ref.current as RStyleArray).style(f, 0) as Style[]).length).toBe(2);
- let style = (RStyle.getStyle(ref) as (Feature) => Style[])(f);
+ const styleArray = await act(() => {
+ return (ref.current as RStyleArray).style(f, 0) as Style[];
+ });
+ expect(styleArray.length).toBe(2);
+ let style = await act(() => {
+ return (RStyle.getStyle(ref) as (Feature) => Style[])(f);
+ });
expect(style[0].getText()?.getText()).toBe('text1');
expect(style[1].getStroke()?.getWidth()).toBe(3);
f.set('name', 'text2');
- style = (RStyle.getStyle(ref) as (Feature) => Style[])(f);
+ style = await act(() => {
+ return (RStyle.getStyle(ref) as (Feature) => Style[])(f);
+ });
expect(style[0].getText()?.getText()).toBe('text2');
rerender(
', () => {
/>
);
f.set('name', 'text3');
- style = (RStyle.getStyle(ref) as (Feature) => Style[])(f);
+ style = await act(() => {
+ return (RStyle.getStyle(ref) as (Feature) => Style[])(f);
+ });
expect(style[1].getText()?.getText()).toBe('text3');
unmount();
});
diff --git a/webpack.config.ts b/webpack.config.ts
index 3c2f0c96..a314e801 100644
--- a/webpack.config.ts
+++ b/webpack.config.ts
@@ -5,18 +5,10 @@ import 'webpack-dev-server';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin';
import {TsconfigPathsPlugin} from 'tsconfig-paths-webpack-plugin';
-import React from 'react';
const webpackConfig = (env): webpack.Configuration => {
- let reactMajorVersion = +React.version.split('.')[0];
- if (reactMajorVersion >= 18) {
- console.log('React 18 detected');
- } else {
- console.log('React 16/17 detected');
- }
-
const conf: webpack.Configuration = {
- entry: reactMajorVersion >= 18 ? './examples/index-react18.tsx' : './examples/index.tsx',
+ entry: './examples/index.tsx',
...(env.production || !env.development ? {} : {devtool: 'eval-source-map'}),
resolve: {
alias: {
@@ -83,15 +75,6 @@ const webpackConfig = (env): webpack.Configuration => {
}
};
- if (reactMajorVersion < 18) {
- // This is needed for React 16/17 as otherwise ts-loader
- // will pick `index-react18.tsx` and will fail transpiling it
- conf.module!.rules!.unshift({
- test: /index-react18\.tsx?$/,
- loader: 'null-loader'
- });
- }
-
if (!env.development) {
conf.plugins!.push(new ForkTsCheckerWebpackPlugin());
}