diff --git a/package-lock.json b/package-lock.json index e1007f3..66f1613 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "babel-plugin-import-map": "^1.0.0", "es-module-shims": "^2.0.0", "esbuild": "^0.17.14", + "lodash": "^4.17.21", "raw-loader": "^4.0.2", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/package.json b/package.json index 841b644..003488b 100644 --- a/package.json +++ b/package.json @@ -55,6 +55,7 @@ "babel-plugin-import-map": "^1.0.0", "es-module-shims": "^2.0.0", "esbuild": "^0.17.14", + "lodash": "^4.17.21", "raw-loader": "^4.0.2", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/src/widget.tsx b/src/widget.tsx index a58c03a..2a74735 100644 --- a/src/widget.tsx +++ b/src/widget.tsx @@ -29,6 +29,7 @@ import { transform } from "sucrase"; import { ErrorBoundary, JupyterWidget } from "./components"; import { Root } from "react-dom/client"; import { ModelDestroyOptions } from "backbone"; +import { isEqual } from "lodash"; declare function importShim( specifier: string, @@ -728,6 +729,24 @@ export class ReactModel extends DOMWidgetModel { } this.listenTo(this, `change:${key}`, updateChildren); } + // If props or children were updated while we were initializing the view, + // we want to do a rerender + const checkPropsChange = async () => { + const [currentProps, currentChildren] = await Promise.all([ + replaceWidgetWithComponent(this.get("props"), get_model), + replaceWidgetWithComponent( + { children: this.get("children") }, + get_model, + ), + ]); + if (!isEqual(currentProps, initialModelProps)) { + updateModelProps(); + } + if (!isEqual(currentChildren, initialChildrenComponents)) { + updateChildren(); + } + }; + this.enqueue(checkPropsChange); return () => { this.stopListening(this, "change:props", updateModelProps); this.stopListening(this, "change:children", updateChildren); diff --git a/tests/ui/children_test.py b/tests/ui/basics_test.py similarity index 71% rename from tests/ui/children_test.py rename to tests/ui/basics_test.py index b62fbe7..8054a08 100644 --- a/tests/ui/children_test.py +++ b/tests/ui/basics_test.py @@ -58,3 +58,27 @@ def on_click(event_data): button.click() # not per se a direct child page_session.locator(".test-button >> .test-html").wait_for() + + +def test_update_children_after_create(solara_test, page_session: playwright.sync_api.Page): + b = ipyreact.Widget( + _type="button", + children=["Initial"], + props={"class": "test-button"}, + ) + b.children = ["Updated"] + + display(b) + page_session.locator(".test-button >> text=Updated").wait_for() + + +def test_update_props_after_create(solara_test, page_session: playwright.sync_api.Page): + b = ipyreact.Widget( + _type="button", + children=["Button"], + props={"class": "test-button-initial"}, + ) + b.props = {"class": "test-button-updated"} + + display(b) + page_session.locator(".test-button-updated >> text=Button").wait_for()