diff --git a/src/React/CHANGELOG.md b/src/React/CHANGELOG.md index 77b48ddca31..afa1f15d12b 100644 --- a/src/React/CHANGELOG.md +++ b/src/React/CHANGELOG.md @@ -1,5 +1,11 @@ # CHANGELOG +## 2.21.0 + +- Add Turbo `data-turbo-permanent` support. Unmounting the React component now uses + a delay and can be prevented if the Stimulus controller is `deconnected` and + immediately `connected` again. + ## 2.13.2 - Revert "Change JavaScript package to `type: module`" diff --git a/src/React/assets/dist/render_controller.d.ts b/src/React/assets/dist/render_controller.d.ts index fc7b1dd4371..5afca0e679b 100644 --- a/src/React/assets/dist/render_controller.d.ts +++ b/src/React/assets/dist/render_controller.d.ts @@ -7,8 +7,10 @@ export default class extends Controller { component: StringConstructor; props: ObjectConstructor; }; + private unmountTimeoutId; connect(): void; disconnect(): void; _renderReactElement(reactElement: ReactElement): void; + _unmountReactElement(): void; private dispatchEvent; } diff --git a/src/React/assets/dist/render_controller.js b/src/React/assets/dist/render_controller.js index 5b8f9fb8e7c..4175d59a6f7 100644 --- a/src/React/assets/dist/render_controller.js +++ b/src/React/assets/dist/render_controller.js @@ -40,6 +40,7 @@ var clientExports = requireClient(); class default_1 extends Controller { connect() { + clearTimeout(this.unmountTimeoutId); const props = this.propsValue ? this.propsValue : null; this.dispatchEvent('connect', { component: this.componentValue, props: props }); if (!this.componentValue) { @@ -54,11 +55,8 @@ class default_1 extends Controller { }); } disconnect() { - this.element.root.unmount(); - this.dispatchEvent('unmount', { - component: this.componentValue, - props: this.propsValue ? this.propsValue : null, - }); + clearTimeout(this.unmountTimeoutId); + this.unmountTimeoutId = setTimeout(this._unmountReactElement.bind(this), 50); } _renderReactElement(reactElement) { const element = this.element; @@ -67,6 +65,13 @@ class default_1 extends Controller { } element.root.render(reactElement); } + _unmountReactElement() { + this.element.root.unmount(); + this.dispatchEvent('unmount', { + component: this.componentValue, + props: this.propsValue ? this.propsValue : null, + }); + } dispatchEvent(name, payload) { this.dispatch(name, { detail: payload, prefix: 'react' }); } diff --git a/src/React/assets/src/render_controller.ts b/src/React/assets/src/render_controller.ts index 595b5a6ae22..2e9b537a307 100644 --- a/src/React/assets/src/render_controller.ts +++ b/src/React/assets/src/render_controller.ts @@ -19,8 +19,11 @@ export default class extends Controller { component: String, props: Object, }; - + private unmountTimeoutId: any; + connect() { + clearTimeout(this.unmountTimeoutId); + const props = this.propsValue ? this.propsValue : null; this.dispatchEvent('connect', { component: this.componentValue, props: props }); @@ -40,11 +43,8 @@ export default class extends Controller { } disconnect() { - (this.element as any).root.unmount(); - this.dispatchEvent('unmount', { - component: this.componentValue, - props: this.propsValue ? this.propsValue : null, - }); + clearTimeout(this.unmountTimeoutId); + this.unmountTimeoutId = setTimeout(this._unmountReactElement.bind(this), 50); } _renderReactElement(reactElement: ReactElement) { @@ -57,6 +57,14 @@ export default class extends Controller { element.root.render(reactElement); } + + _unmountReactElement() { + (this.element as any).root.unmount(); + this.dispatchEvent('unmount', { + component: this.componentValue, + props: this.propsValue ? this.propsValue : null, + }); + } private dispatchEvent(name: string, payload: any) { this.dispatch(name, { detail: payload, prefix: 'react' });