diff --git a/.circleci/config.yml b/.circleci/config.yml
index bcbd27b390..20b189a46c 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -204,6 +204,24 @@ jobs:
- store_artifacts:
path: /tmp/dash_artifacts
+ test-39-react-18:
+ <<: *test
+ docker:
+ - image: cimg/python:3.9.9-browsers
+ auth:
+ username: dashautomation
+ password: $DASH_PAT_DOCKERHUB
+ environment:
+ PERCY_ENABLE: 0
+ PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: True
+ PYVERSION: python39
+ REDIS_URL: redis://localhost:6379
+ REACT_VERSION: "18.2.0"
+ - image: cimg/redis:6.2.6
+ auth:
+ username: dashautomation
+ password: $DASH_PAT_DOCKERHUB
+
test-36:
<<: *test
docker:
@@ -258,7 +276,7 @@ jobs:
PYVERSION: python36
PERCY_ENABLE: 0
- dcc-test-39: &dcc-test
+ dcc-39: &dcc-test
working_directory: ~/dash
docker:
- image: cimg/python:3.9.9-browsers
@@ -302,7 +320,19 @@ jobs:
- store_artifacts:
path: /tmp/dash_artifacts
- dcc-test-36:
+ dcc-39-react-18:
+ <<: *dcc-test
+ docker:
+ - image: cimg/python:3.9.9-browsers
+ auth:
+ username: dashautomation
+ password: $DASH_PAT_DOCKERHUB
+ environment:
+ PYVERSION: python39
+ PERCY_ENABLE: 0
+ REACT_VERSION: "18.2.0"
+
+ dcc-36:
<<: *dcc-test
docker:
- image: cimg/python:3.6.15-browsers
@@ -313,7 +343,7 @@ jobs:
PYVERSION: python36
PERCY_ENABLE: 0
- html-python-39: &html-test
+ html-39: &html-test
working_directory: ~/dash
docker:
- image: cimg/python:3.9.9-browsers
@@ -365,7 +395,19 @@ jobs:
- store_artifacts:
path: /tmp/dash_artifacts
- html-python-36:
+ html-39-react-18:
+ <<: *html-test
+ docker:
+ - image: cimg/python:3.9.9-browsers
+ auth:
+ username: dashautomation
+ password: $DASH_PAT_DOCKERHUB
+ environment:
+ PYVERSION: python39
+ PERCY_ENABLE: 0
+ REACT_VERSION: "18.2.0"
+
+ html-36:
<<: *html-test
docker:
- image: cimg/python:3.6.15-browsers
@@ -376,7 +418,7 @@ jobs:
PYVERSION: python36
PERCY_ENABLE: 0
- table-server-test:
+ table-server: &table-server
working_directory: ~/dash
docker:
- image: cimg/python:3.9.9-browsers
@@ -423,6 +465,15 @@ jobs:
- store_artifacts:
path: /tmp/dash_artifacts
+ table-server-react-18:
+ <<: *table-server
+ docker:
+ - image: cimg/python:3.9.9-browsers
+ environment:
+ PYVERSION: python39
+ PERCY_ENABLE: 0
+ REACT_VERSION: "18.2.0"
+
table-unit-test:
working_directory: ~/dash
docker:
@@ -552,6 +603,9 @@ workflows:
- test-39:
requires:
- install-dependencies-39
+ - test-39-react-18:
+ requires:
+ - install-dependencies-39
- test-36:
requires:
- install-dependencies-36
@@ -563,17 +617,23 @@ workflows:
requires:
- install-dependencies-36
- - dcc-test-39:
+ - dcc-39:
requires:
- install-dependencies-39
- - dcc-test-36:
+ - dcc-39-react-18:
+ requires:
+ - install-dependencies-39
+ - dcc-36:
requires:
- install-dependencies-36
- - html-python-39:
+ - html-39:
+ requires:
+ - install-dependencies-39
+ - html-39-react-18:
requires:
- install-dependencies-39
- - html-python-36:
+ - html-36:
requires:
- install-dependencies-36
@@ -584,16 +644,19 @@ workflows:
requires:
- install-dependencies-39
- table-visual-test
- - table-server-test:
+ - table-server:
+ requires:
+ - install-dependencies-39
+ - table-server-react-18:
requires:
- install-dependencies-39
- percy/finalize_all:
requires:
- test-39
- - dcc-test-39
- - html-python-39
- - table-server-test
+ - dcc-39
+ - html-39
+ - table-server
- artifacts:
requires:
- percy/finalize_all
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 082123f444..6aaf8a9d9f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
- [#2417](https://github.com/plotly/dash/pull/2417) Add wait_timeout property to customize the behavior of the default wait timeout used for by wait_for_page, fix [#1595](https://github.com/plotly/dash/issues/1595)
- [#2417](https://github.com/plotly/dash/pull/2417) Add the element target text for wait_for_text* error message, fix [#945](https://github.com/plotly/dash/issues/945)
- [#2425](https://github.com/plotly/dash/pull/2425) Add `add_log_handler=True` to Dash init, if you don't want a log stream handler at all.
+- [#2260](https://github.com/plotly/dash/pull/2260) Experimental support for React 18. The default is still React v16.14.0, but to use React 18 you can either set the environment variable `REACT_VERSION=18.2.0` before running your app, or inside the app call `dash._dash_renderer._set_react_version("18.2.0")`. THIS FEATURE IS EXPERIMENTAL. It has not been tested with component suites outside the Dash core, and we may add or remove available React versions in any future release.
## Fixed
diff --git a/components/dash-table/tests/selenium/test_header.py b/components/dash-table/tests/selenium/test_header.py
index 5d1dd7626c..4bb3315b17 100644
--- a/components/dash-table/tests/selenium/test_header.py
+++ b/components/dash-table/tests/selenium/test_header.py
@@ -23,7 +23,7 @@ def get_app(props=dict()):
return app
-def get_sigle_row_app(props=dict()):
+def get_single_row_app(props=dict()):
app = dash.Dash(__name__)
baseProps = get_props()
@@ -115,7 +115,7 @@ def test_head003_preserves_column_name_on_cancel(test):
def test_head004_change_single_row_header(test):
- test.start_server(get_sigle_row_app())
+ test.start_server(get_single_row_app())
target = test.table("table")
diff --git a/dash/_dash_renderer.py b/dash/_dash_renderer.py
index 43d5588d6b..1af05e4ab7 100644
--- a/dash/_dash_renderer.py
+++ b/dash/_dash_renderer.py
@@ -1,39 +1,64 @@
+import os
+
__version__ = "1.14.5"
-_js_dist_dependencies = [
- {
- "external_url": {
- "prod": [
- "https://unpkg.com/@babel/polyfill@7.12.1/dist/polyfill.min.js",
- "https://unpkg.com/react@16.14.0/umd/react.production.min.js",
- "https://unpkg.com/react-dom@16.14.0/umd/react-dom.production.min.js",
- "https://unpkg.com/prop-types@15.8.1/prop-types.min.js",
- ],
- "dev": [
- "https://unpkg.com/@babel/polyfill@7.12.1/dist/polyfill.min.js",
- "https://unpkg.com/react@16.14.0/umd/react.development.js",
- "https://unpkg.com/react-dom@16.14.0/umd/react-dom.development.js",
- "https://unpkg.com/prop-types@15.8.1/prop-types.js",
- ],
- },
- "relative_package_path": {
- "prod": [
- "deps/polyfill@7.12.1.min.js",
- "deps/react@16.14.0.min.js",
- "deps/react-dom@16.14.0.min.js",
- "deps/prop-types@15.8.1.min.js",
- ],
- "dev": [
- "deps/polyfill@7.12.1.min.js",
- "deps/react@16.14.0.js",
- "deps/react-dom@16.14.0.js",
- "deps/prop-types@15.8.1.js",
- ],
- },
- "namespace": "dash",
- }
-]
+_available_react_versions = {"16.14.0", "18.2.0"}
+_available_reactdom_versions = {"16.14.0", "18.2.0"}
+_js_dist_dependencies = [] # to be set by _set_react_version
+
+
+def _set_react_version(v_react, v_reactdom=None):
+ if not v_reactdom:
+ v_reactdom = v_react
+
+ react_err = f"looking for one of {_available_react_versions}, found {v_react}"
+ reactdom_err = (
+ f"looking for one of {_available_reactdom_versions}, found {v_reactdom}"
+ )
+ assert v_react in _available_react_versions, react_err
+ assert v_reactdom in _available_reactdom_versions, reactdom_err
+
+ _js_dist_dependencies[:] = [
+ {
+ "external_url": {
+ "prod": [
+ "https://unpkg.com/@babel/polyfill@7.12.1/dist/polyfill.min.js",
+ f"https://unpkg.com/react@{v_react}/umd/react.production.min.js",
+ f"https://unpkg.com/react-dom@{v_reactdom}/umd/react-dom.production.min.js",
+ "https://unpkg.com/prop-types@15.8.1/prop-types.min.js",
+ ],
+ "dev": [
+ "https://unpkg.com/@babel/polyfill@7.12.1/dist/polyfill.min.js",
+ f"https://unpkg.com/react@{v_react}/umd/react.development.js",
+ f"https://unpkg.com/react-dom@{v_reactdom}/umd/react-dom.development.js",
+ "https://unpkg.com/prop-types@15.8.1/prop-types.js",
+ ],
+ },
+ "relative_package_path": {
+ "prod": [
+ "deps/polyfill@7.12.1.min.js",
+ f"deps/react@{v_react}.min.js",
+ f"deps/react-dom@{v_reactdom}.min.js",
+ "deps/prop-types@15.8.1.min.js",
+ ],
+ "dev": [
+ "deps/polyfill@7.12.1.min.js",
+ f"deps/react@{v_react}.js",
+ f"deps/react-dom@{v_reactdom}.js",
+ "deps/prop-types@15.8.1.js",
+ ],
+ },
+ "namespace": "dash",
+ }
+ ]
+
+_env_react_version = os.getenv("REACT_VERSION")
+if _env_react_version:
+ _set_react_version(_env_react_version)
+ print(f"EXPERIMENTAL: Using react version from env: {_env_react_version}")
+else:
+ _set_react_version("16.14.0", "16.14.0")
_js_dist = [
{
diff --git a/dash/dash-renderer/init.template b/dash/dash-renderer/init.template
index 4395a8a29a..395a327b49 100644
--- a/dash/dash-renderer/init.template
+++ b/dash/dash-renderer/init.template
@@ -1,39 +1,63 @@
+import os
+
__version__ = "$version"
-_js_dist_dependencies = [
- {
- "external_url": {
- "prod": [
- "https://unpkg.com/@babel/polyfill@$polyfill/dist/polyfill.min.js",
- "https://unpkg.com/react@$react/umd/react.production.min.js",
- "https://unpkg.com/react-dom@$reactdom/umd/react-dom.production.min.js",
- "https://unpkg.com/prop-types@$proptypes/prop-types.min.js",
- ],
- "dev": [
- "https://unpkg.com/@babel/polyfill@$polyfill/dist/polyfill.min.js",
- "https://unpkg.com/react@$react/umd/react.development.js",
- "https://unpkg.com/react-dom@$reactdom/umd/react-dom.development.js",
- "https://unpkg.com/prop-types@$proptypes/prop-types.js",
- ],
- },
- "relative_package_path": {
- "prod": [
- "deps/polyfill@$polyfill.min.js",
- "deps/react@$react.min.js",
- "deps/react-dom@$reactdom.min.js",
- "deps/prop-types@$proptypes.min.js",
- ],
- "dev": [
- "deps/polyfill@$polyfill.min.js",
- "deps/react@$react.js",
- "deps/react-dom@$reactdom.js",
- "deps/prop-types@$proptypes.js",
- ],
- },
- "namespace": "dash",
- }
-]
+_available_react_versions = {"$react", $extra_react_versions}
+_available_reactdom_versions = {"$reactdom", $extra_reactdom_versions}
+_js_dist_dependencies = [] # to be set by _set_react_version
+
+
+def _set_react_version(v_react, v_reactdom=None):
+ if not v_reactdom:
+ v_reactdom = v_react
+
+ react_err = f"looking for one of {_available_react_versions}, found {v_react}"
+ reactdom_err = (
+ f"looking for one of {_available_reactdom_versions}, found {v_reactdom}"
+ )
+ assert v_react in _available_react_versions, react_err
+ assert v_reactdom in _available_reactdom_versions, reactdom_err
+
+ _js_dist_dependencies[:] = [
+ {
+ "external_url": {
+ "prod": [
+ "https://unpkg.com/@babel/polyfill@$polyfill/dist/polyfill.min.js",
+ f"https://unpkg.com/react@{v_react}/umd/react.production.min.js",
+ f"https://unpkg.com/react-dom@{v_reactdom}/umd/react-dom.production.min.js",
+ "https://unpkg.com/prop-types@$proptypes/prop-types.min.js",
+ ],
+ "dev": [
+ "https://unpkg.com/@babel/polyfill@$polyfill/dist/polyfill.min.js",
+ f"https://unpkg.com/react@{v_react}/umd/react.development.js",
+ f"https://unpkg.com/react-dom@{v_reactdom}/umd/react-dom.development.js",
+ "https://unpkg.com/prop-types@$proptypes/prop-types.js",
+ ],
+ },
+ "relative_package_path": {
+ "prod": [
+ "deps/polyfill@$polyfill.min.js",
+ f"deps/react@{v_react}.min.js",
+ f"deps/react-dom@{v_reactdom}.min.js",
+ "deps/prop-types@$proptypes.min.js",
+ ],
+ "dev": [
+ "deps/polyfill@$polyfill.min.js",
+ f"deps/react@{v_react}.js",
+ f"deps/react-dom@{v_reactdom}.js",
+ "deps/prop-types@$proptypes.js",
+ ],
+ },
+ "namespace": "dash",
+ }
+ ]
+
+_env_react_version = os.getenv("REACT_VERSION")
+if _env_react_version:
+ _set_react_version(_env_react_version)
+else:
+ _set_react_version("$react", "$reactdom")
_js_dist = [
{
diff --git a/dash/dash-renderer/src/DashRenderer.js b/dash/dash-renderer/src/DashRenderer.js
index 2a4d7b0bf1..d1ce51a5c7 100644
--- a/dash/dash-renderer/src/DashRenderer.js
+++ b/dash/dash-renderer/src/DashRenderer.js
@@ -1,14 +1,19 @@
import React from 'react';
import ReactDOM from 'react-dom';
+
import AppProvider from './AppProvider.react';
class DashRenderer {
constructor(hooks) {
// render Dash Renderer upon initialising!
- ReactDOM.render(
- ,
- document.getElementById('react-entry-point')
- );
+ const container = document.getElementById('react-entry-point');
+
+ if (ReactDOM.createRoot) {
+ const root = ReactDOM.createRoot(container);
+ root.render();
+ } else {
+ ReactDOM.render(, container);
+ }
}
}
diff --git a/dash/development/build_process.py b/dash/development/build_process.py
index ff1bcffcf6..2e9901def1 100644
--- a/dash/development/build_process.py
+++ b/dash/development/build_process.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
import os
import sys
import json
@@ -7,6 +6,7 @@
import logging
import coloredlogs
import fire
+import requests
from .._utils import run_command_with_process, compute_md5, job
@@ -107,7 +107,7 @@ def digest(self):
)
@job("copy and generate the bundles")
- def bundles(self, build=None):
+ def bundles(self, build=None): # pylint:disable=too-many-locals
if not os.path.exists(self.deps_folder):
try:
os.makedirs(self.deps_folder)
@@ -124,19 +124,32 @@ def bundles(self, build=None):
"package": self.name.replace(" ", "_").replace("-", "_"),
}
- for scope, name, subfolder, filename, target in self.deps_info:
+ for scope, name, subfolder, filename, extras in self.deps_info:
version = self.deps["/".join(filter(None, [scope, name]))]["version"]
- versions[name.replace("-", "").replace(".", "")] = version
+ name_squashed = name.replace("-", "").replace(".", "")
+ versions[name_squashed] = version
logger.info("copy npm dependency => %s", filename)
ext = "min.js" if "min" in filename.split(".") else "js"
- target = target.format(version) if target else f"{name}@{version}.{ext}"
+ target = f"{name}@{version}.{ext}"
shutil.copyfile(
self._concat(self.npm_modules, scope, name, subfolder, filename),
self._concat(self.deps_folder, target),
)
+ if extras:
+ extras_str = '", "'.join(extras)
+ versions[f"extra_{name_squashed}_versions"] = f'"{extras_str}"'
+
+ for extra_version in extras:
+ url = f"https://unpkg.com/{name}@{extra_version}/umd/{filename}"
+ res = requests.get(url)
+ extra_target = f"{name}@{extra_version}.{ext}"
+ extra_path = self._concat(self.deps_folder, extra_target)
+ with open(extra_path, "wb") as fp:
+ fp.write(res.content)
+
_script = "build:dev" if build == "local" else "build:js"
logger.info("run `npm run %s`", _script)
os.chdir(self.main)
@@ -146,25 +159,23 @@ def bundles(self, build=None):
with open(self._concat(self.main, "init.template"), encoding="utf-8") as fp:
t = string.Template(fp.read())
- with open(
- self._concat(self.deps_folder, os.pardir, "_dash_renderer.py"),
- "w",
- encoding="utf-8",
- ) as fp:
+ renderer_init = self._concat(self.deps_folder, os.pardir, "_dash_renderer.py")
+ with open(renderer_init, "w", encoding="utf-8") as fp:
fp.write(t.safe_substitute(versions))
class Renderer(BuildProcess):
def __init__(self):
"""dash-renderer's path is binding with the dash folder hierarchy."""
+ extras = ["18.2.0"] # versions to include beyond what's in package.json
super().__init__(
self._concat(os.path.dirname(__file__), os.pardir, "dash-renderer"),
(
("@babel", "polyfill", "dist", "polyfill.min.js", None),
- (None, "react", "umd", "react.production.min.js", None),
- (None, "react", "umd", "react.development.js", None),
- (None, "react-dom", "umd", "react-dom.production.min.js", None),
- (None, "react-dom", "umd", "react-dom.development.js", None),
+ (None, "react", "umd", "react.production.min.js", extras),
+ (None, "react", "umd", "react.development.js", extras),
+ (None, "react-dom", "umd", "react-dom.production.min.js", extras),
+ (None, "react-dom", "umd", "react-dom.development.js", extras),
(None, "prop-types", None, "prop-types.min.js", None),
(None, "prop-types", None, "prop-types.js", None),
),