Repository: DevExpress/devextreme-react
Pull request #675 (2022-06-10 09:05:30)

Fix react 18
cherry pick https://github.com/DevExpress/devextreme-react/pull/670, https://github.com/DevExpress/devextreme-react/pull/663 and https://github.com/DevExpress/devextreme-react/pull/664


Patch:
diff --git a/packages/devextreme-react-generator/src/component-generator.ts b/packages/devextreme-react-generator/src/component-generator.ts
index 754dcce..747af79 100644
--- a/packages/devextreme-react-generator/src/component-generator.ts
+++ b/packages/devextreme-react-generator/src/component-generator.ts
@@ -243,7 +243,7 @@ const renderNestedComponent: (model: {
     + '// <#= owner #>\n'
 + '<#~#>'
 
-+ 'interface <#= it.propsType #> <#= it.renderedType #>\n'
++ 'type <#= it.propsType #> = React.PropsWithChildren<<#= it.renderedType #>>\n'
 
 + 'class <#= it.className #> extends NestedOption<<#= it.propsType #>> {'}${
     L1}public static OptionName = "<#= it.optionName #>";`
@@ -298,7 +298,7 @@ const renderOptionsInterface: (model: {
     type: string;
   }>;
 }) => string = createTempate(
-  'interface <#= it.optionsName #> extends IOptions, IHtmlOptions {\n'
+  'type <#= it.optionsName #> = React.PropsWithChildren<IOptions & IHtmlOptions & {\n'
 
 + '<#~ it.templates :template #>'
     + `  <#= template.render #>?: ${TYPE_RENDER};\n`
@@ -314,7 +314,7 @@ const renderOptionsInterface: (model: {
     + '  <#= prop.name #>?: <#= prop.type #>;\n'
 + '<#~#>'
 
-+ '}',
++ '}>',
 );
 
 const renderComponent: (model: {
diff --git a/packages/devextreme-react/src/core/component-base.ts b/packages/devextreme-react/src/core/component-base.ts
index f024aa9..1a39433 100644
--- a/packages/devextreme-react/src/core/component-base.ts
+++ b/packages/devextreme-react/src/core/component-base.ts
@@ -54,6 +54,8 @@ abstract class ComponentBase<P extends IHtmlOptions> extends React.PureComponent
 
   protected readonly independentEvents: string[];
 
+  private _childNode: Node;
+
   private _templatesRendererRef: TemplatesRenderer | null;
 
   private _templatesStore: TemplatesStore;
@@ -82,6 +84,12 @@ abstract class ComponentBase<P extends IHtmlOptions> extends React.PureComponent
   }
 
   public componentDidMount(): void {
+    if (this._childNode) {
+      this._element.appendChild(this._childNode);
+    }
+    if (!this._childNode && this._element.childNodes.length) {
+      this._childNode = this._element.childNodes[0];
+    }
     this._updateCssClasses(null, this.props);
   }
 
@@ -97,6 +105,7 @@ abstract class ComponentBase<P extends IHtmlOptions> extends React.PureComponent
 
   public componentWillUnmount(): void {
     if (this._instance) {
+      this._childNode?.parentNode?.removeChild(this._childNode);
       events.triggerHandler(this._element, DX_REMOVE_EVENT);
       this._instance.dispose();
     }
diff --git a/packages/devextreme-react/src/core/dx-template.ts b/packages/devextreme-react/src/core/dx-template.ts
index 65efd18..6cdf8b6 100644
--- a/packages/devextreme-react/src/core/dx-template.ts
+++ b/packages/devextreme-react/src/core/dx-template.ts
@@ -34,6 +34,7 @@ function createDxTemplate(
       const prevTemplateId = renderedTemplates.get(key);
 
       let templateId: string;
+
       if (prevTemplateId) {
         templateId = prevTemplateId;
       } else {
@@ -43,7 +44,6 @@ function createDxTemplate(
           renderedTemplates.set(key, templateId);
         }
       }
-
       templatesStore.add(templateId, () => {
         const props: ITemplateArgs = {
           data: data.model,
@@ -57,10 +57,13 @@ function createDxTemplate(
             content: contentProvider(props),
             container,
             onRemoved: () => {
-              templatesStore.remove(templateId);
+              templatesStore.setDeferredRemove(templateId, true);
               renderedTemplates.delete({ key1: data.model, key2: container });
             },
-            onRendered: data.onRendered,
+            onDidMount: () => {
+              templatesStore.setDeferredRemove(templateId, false);
+              data.onRendered?.();
+            },
             key: templateId,
           },
         ) as any as TemplateWrapper;
diff --git a/packages/devextreme-react/src/core/template-wrapper.ts b/packages/devextreme-react/src/core/template-wrapper.ts
index 3ff1e86..ac743e4 100644
--- a/packages/devextreme-react/src/core/template-wrapper.ts
+++ b/packages/devextreme-react/src/core/template-wrapper.ts
@@ -8,7 +8,7 @@ interface ITemplateWrapperProps {
   content: any;
   container: Element;
   onRemoved: () => void;
-  onRendered?: () => void;
+  onDidMount?: () => void;
   key: string;
 }
 
@@ -38,7 +38,7 @@ class TemplateWrapper extends React.PureComponent<ITemplateWrapperProps, ITempla
 
   public componentDidMount(): void {
     this._subscribeOnRemove();
-    this.props.onRendered?.();
+    this.props.onDidMount?.();
   }
 
   public componentDidUpdate(): void {
diff --git a/packages/devextreme-react/src/core/templates-store.ts b/packages/devextreme-react/src/core/templates-store.ts
index 1fd9097..3cf328c 100644
--- a/packages/devextreme-react/src/core/templates-store.ts
+++ b/packages/devextreme-react/src/core/templates-store.ts
@@ -1,7 +1,11 @@
 import { TemplateWrapper, TemplateWrapperRenderer } from './template-wrapper';
 
+interface TemplateInfo {
+  template: TemplateWrapperRenderer;
+  isDeferredRemove: boolean;
+}
 class TemplatesStore {
-  private readonly _templates: Record<string, TemplateWrapperRenderer> = {};
+  private readonly _templates: Record<string, TemplateInfo> = {};
 
   private readonly _onTemplateAdded: () => void;
 
@@ -10,17 +14,28 @@ class TemplatesStore {
   }
 
   public add(templateId: string, templateFunc: TemplateWrapperRenderer): void {
-    this._templates[templateId] = templateFunc;
+    this._templates[templateId] = { template: templateFunc, isDeferredRemove: false };
     this._onTemplateAdded();
   }
 
-  public remove(templateId: string): void {
-    delete this._templates[templateId];
+  public setDeferredRemove(templateId: string, isDeferredRemove: boolean): void {
+    if (this._templates[templateId]) {
+      this._templates[templateId].isDeferredRemove = isDeferredRemove;
+    }
+  }
+
+  private removeDefferedTemplate(): void {
+    Object.entries(this._templates)
+      .filter(([, templateInfo]) => (templateInfo.isDeferredRemove))
+      .forEach(([templateId]) => {
+        delete this._templates[templateId];
+      });
   }
 
   public renderWrappers(): TemplateWrapper[] {
+    this.removeDefferedTemplate();
     return Object.getOwnPropertyNames(this._templates).map(
-      (templateId) => this._templates[templateId](),
+      (templateId) => this._templates[templateId].template(),
     );
   }
 }


Test patch:
diff --git a/packages/devextreme-react-generator/src/component-generator.test.ts b/packages/devextreme-react-generator/src/component-generator.test.ts
index 65e64b7..c7f026a 100644
--- a/packages/devextreme-react-generator/src/component-generator.test.ts
+++ b/packages/devextreme-react-generator/src/component-generator.test.ts
@@ -9,8 +9,8 @@ import dxCLASS_NAME, {
 
 import { Component as BaseComponent, IHtmlOptions } from "BASE_COMPONENT_PATH";
 
-interface ICLASS_NAMEOptions extends IOptions, IHtmlOptions {
-}
+type ICLASS_NAMEOptions = React.PropsWithChildren<IOptions & IHtmlOptions & {
+}>
 
 class CLASS_NAME extends BaseComponent<ICLASS_NAMEOptions> {
 
@@ -84,11 +84,11 @@ import dxCLASS_NAME, {
 
 import { Component as BaseComponent, IHtmlOptions } from "BASE_COMPONENT_PATH";
 
-interface ICLASS_NAMEOptions extends IOptions, IHtmlOptions {
+type ICLASS_NAMEOptions = React.PropsWithChildren<IOptions & IHtmlOptions & {
   optionRender?: (...params: any) => React.ReactNode;
   optionComponent?: React.ComponentType<any>;
   optionKeyFn?: (data: any) => string;
-}
+}>
 
 class CLASS_NAME extends BaseComponent<ICLASS_NAMEOptions> {
 
@@ -133,14 +133,14 @@ import dxCLASS_NAME, {
 
 import { Component as BaseComponent, IHtmlOptions } from "BASE_COMPONENT_PATH";
 
-interface ICLASS_NAMEOptions extends IOptions, IHtmlOptions {
+type ICLASS_NAMEOptions = React.PropsWithChildren<IOptions & IHtmlOptions & {
   optionRender?: (...params: any) => React.ReactNode;
   optionComponent?: React.ComponentType<any>;
   optionKeyFn?: (data: any) => string;
   anotherOptionRender?: (...params: any) => React.ReactNode;
   anotherOptionComponent?: React.ComponentType<any>;
   anotherOptionKeyFn?: (data: any) => string;
-}
+}>
 
 class CLASS_NAME extends BaseComponent<ICLASS_NAMEOptions> {
 
@@ -190,11 +190,11 @@ import dxCLASS_NAME, {
 
 import { Component as BaseComponent, IHtmlOptions } from "BASE_COMPONENT_PATH";
 
-interface ICLASS_NAMEOptions extends IOptions, IHtmlOptions {
+type ICLASS_NAMEOptions = React.PropsWithChildren<IOptions & IHtmlOptions & {
   render?: (...params: any) => React.ReactNode;
   component?: React.ComponentType<any>;
   keyFn?: (data: any) => string;
-}
+}>
 
 class CLASS_NAME extends BaseComponent<ICLASS_NAMEOptions> {
 
@@ -241,10 +241,10 @@ import dxCLASS_NAME, {
 
 import { Component as BaseComponent, IHtmlOptions } from "BASE_COMPONENT_PATH";
 
-interface ICLASS_NAMEOptions extends IOptions, IHtmlOptions {
+type ICLASS_NAMEOptions = React.PropsWithChildren<IOptions & IHtmlOptions & {
   defaultOption1?: someType;
   onOption1Change?: (value: someType) => void;
-}
+}>
 
 class CLASS_NAME extends BaseComponent<ICLASS_NAMEOptions> {
 
@@ -290,12 +290,12 @@ import dxCLASS_NAME, {
 
 import { Component as BaseComponent, IHtmlOptions } from "BASE_COMPONENT_PATH";
 
-interface ICLASS_NAMEOptions extends IOptions, IHtmlOptions {
+type ICLASS_NAMEOptions = React.PropsWithChildren<IOptions & IHtmlOptions & {
   defaultOption1?: someType;
   defaultOption2?: anotherType;
   onOption1Change?: (value: someType) => void;
   onOption2Change?: (value: anotherType) => void;
-}
+}>
 
 class CLASS_NAME extends BaseComponent<ICLASS_NAMEOptions> {
 
@@ -343,10 +343,10 @@ import dxCLASS_NAME, {
 
 import { Component as BaseComponent, IHtmlOptions } from "BASE_COMPONENT_PATH";
 
-interface ICLASS_NAMEOptions extends IOptions, IHtmlOptions {
+type ICLASS_NAMEOptions = React.PropsWithChildren<IOptions & IHtmlOptions & {
   defaultOption1?: someType;
   onOption1Change?: (value: someType) => void;
-}
+}>
 
 class CLASS_NAME extends BaseComponent<ICLASS_NAMEOptions> {
 
@@ -397,8 +397,8 @@ import dxCLASS_NAME, {
 import { Component as BaseComponent, IHtmlOptions } from "BASE_COMPONENT_PATH";
 import NestedOption from "CONFIG_COMPONENT_PATH";
 
-interface ICLASS_NAMEOptions extends IOptions, IHtmlOptions {
-}
+type ICLASS_NAMEOptions = React.PropsWithChildren<IOptions & IHtmlOptions & {
+}>
 
 class CLASS_NAME extends BaseComponent<ICLASS_NAMEOptions> {
 
@@ -412,7 +412,7 @@ class CLASS_NAME extends BaseComponent<ICLASS_NAMEOptions> {
 
 // owners:
 // CLASS_NAME
-interface IOpt_1_ComponentProps {
+type IOpt_1_ComponentProps = React.PropsWithChildren<{
   sub_opt_2?: TYPE_1;
   sub_opt_3?: TYPE_SUB_3 | {
     sub_sub_opt_4?: TYPE_2;
@@ -420,16 +420,16 @@ interface IOpt_1_ComponentProps {
       sub_sub_sub_opt_6?: TYPE_3;
     };
   };
-}
+}>
 class Opt_1_Component extends NestedOption<IOpt_1_ComponentProps> {
   public static OptionName = "opt_1";
 }
 
 // owners:
 // Opt_1_Component
-interface IOpt_6_SubComponentProps {
+type IOpt_6_SubComponentProps = React.PropsWithChildren<{
   sub_sub_sub_opt_8?: TYPE_4;
-}
+}>
 class Opt_6_SubComponent extends NestedOption<IOpt_6_SubComponentProps> {
   public static OptionName = "sub_sub_opt_7";
 }
@@ -511,8 +511,8 @@ import dxCLASS_NAME, {
 import { Component as BaseComponent, IHtmlOptions } from "BASE_COMPONENT_PATH";
 import NestedOption from "CONFIG_COMPONENT_PATH";
 
-interface ICLASS_NAMEOptions extends IOptions, IHtmlOptions {
-}
+type ICLASS_NAMEOptions = React.PropsWithChildren<IOptions & IHtmlOptions & {
+}>
 
 class CLASS_NAME extends BaseComponent<ICLASS_NAMEOptions> {
 
@@ -526,9 +526,9 @@ class CLASS_NAME extends BaseComponent<ICLASS_NAMEOptions> {
 
 // owners:
 // CLASS_NAME
-interface IOpt_1_ComponentProps {
+type IOpt_1_ComponentProps = React.PropsWithChildren<{
   sub_opt_2?: TYPE_1;
-}
+}>
 class Opt_1_Component extends NestedOption<IOpt_1_ComponentProps> {
   public static OptionName = "opt_1";
   public static IsCollectionItem = true;
@@ -579,8 +579,8 @@ import dxCLASS_NAME, {
 import { Component as BaseComponent, IHtmlOptions } from "BASE_COMPONENT_PATH";
 import NestedOption from "CONFIG_COMPONENT_PATH";
 
-interface ICLASS_NAMEOptions extends IOptions, IHtmlOptions {
-}
+type ICLASS_NAMEOptions = React.PropsWithChildren<IOptions & IHtmlOptions & {
+}>
 
 class CLASS_NAME extends BaseComponent<ICLASS_NAMEOptions> {
 
@@ -594,14 +594,14 @@ class CLASS_NAME extends BaseComponent<ICLASS_NAMEOptions> {
 
 // owners:
 // CLASS_NAME
-interface IOpt_1_ComponentProps {
+type IOpt_1_ComponentProps = React.PropsWithChildren<{
   sub_opt_2?: TYPE_1;
   sub_opt_3?: {
     sub_sub_opt_4?: TYPE_2;
   };
   defaultSub_opt_2?: TYPE_1;
   onSub_opt_2Change?: (value: TYPE_1) => void;
-}
+}>
 class Opt_1_Component extends NestedOption<IOpt_1_ComponentProps> {
   public static OptionName = "opt_1";
   public static DefaultsProps = {
@@ -664,8 +664,8 @@ import dxCLASS_NAME, {
 import { Component as BaseComponent, IHtmlOptions } from "BASE_COMPONENT_PATH";
 import NestedOption from "CONFIG_COMPONENT_PATH";
 
-interface ICLASS_NAMEOptions extends IOptions, IHtmlOptions {
-}
+type ICLASS_NAMEOptions = React.PropsWithChildren<IOptions & IHtmlOptions & {
+}>
 
 class CLASS_NAME extends BaseComponent<ICLASS_NAMEOptions> {
 
@@ -679,12 +679,12 @@ class CLASS_NAME extends BaseComponent<ICLASS_NAMEOptions> {
 
 // owners:
 // CLASS_NAME
-interface IOpt_1_ComponentProps {
+type IOpt_1_ComponentProps = React.PropsWithChildren<{
   optionTemplate?: TYPE_1;
   optionRender?: (...params: any) => React.ReactNode;
   optionComponent?: React.ComponentType<any>;
   optionKeyFn?: (data: any) => string;
-}
+}>
 class Opt_1_Component extends NestedOption<IOpt_1_ComponentProps> {
   public static OptionName = "opt_1";
   public static TemplateProps = [{
@@ -740,8 +740,8 @@ import dxCLASS_NAME, {
 import { Component as BaseComponent, IHtmlOptions } from "BASE_COMPONENT_PATH";
 import NestedOption from "CONFIG_COMPONENT_PATH";
 
-interface ICLASS_NAMEOptions extends IOptions, IHtmlOptions {
-}
+type ICLASS_NAMEOptions = React.PropsWithChildren<IOptions & IHtmlOptions & {
+}>
 
 class CLASS_NAME extends BaseComponent<ICLASS_NAMEOptions> {
 
@@ -755,9 +755,9 @@ class CLASS_NAME extends BaseComponent<ICLASS_NAMEOptions> {
 
 // owners:
 // CLASS_NAME
-interface IOpt_1_ComponentProps {
+type IOpt_1_ComponentProps = React.PropsWithChildren<{
   sub_opt_2?: TYPE_1;
-}
+}>
 class Opt_1_Component extends NestedOption<IOpt_1_ComponentProps> {
   public static OptionName = "opt_1";
   public static IsCollectionItem = true;
@@ -813,8 +813,8 @@ import dxCLASS_NAME, {
 import { Component as BaseComponent, IHtmlOptions } from "BASE_COMPONENT_PATH";
 import NestedOption from "CONFIG_COMPONENT_PATH";
 
-interface ICLASS_NAMEOptions extends IOptions, IHtmlOptions {
-}
+type ICLASS_NAMEOptions = React.PropsWithChildren<IOptions & IHtmlOptions & {
+}>
 
 class CLASS_NAME extends BaseComponent<ICLASS_NAMEOptions> {
 
@@ -828,13 +828,13 @@ class CLASS_NAME extends BaseComponent<ICLASS_NAMEOptions> {
 
 // owners:
 // CLASS_NAME
-interface IOpt_1_ComponentProps {
+type IOpt_1_ComponentProps = React.PropsWithChildren<{
   sub_opt_2?: TYPE_1;
   sub_opt_3?: {
     subsub_1?: TYPE_3;
     subsub_2?: TYPE_4;
   }[];
-}
+}>
 class Opt_1_Component extends NestedOption<IOpt_1_ComponentProps> {
   public static OptionName = "opt_1";
   public static IsCollectionItem = true;
@@ -906,8 +906,8 @@ import dxCLASS_NAME, {
 import * as PropTypes from "prop-types";
 import { Component as BaseComponent, IHtmlOptions } from "BASE_COMPONENT_PATH";
 
-interface ICLASS_NAMEOptions extends IOptions, IHtmlOptions {
-}
+type ICLASS_NAMEOptions = React.PropsWithChildren<IOptions & IHtmlOptions & {
+}>
 
 class CLASS_NAME extends BaseComponent<ICLASS_NAMEOptions> {
 
@@ -954,8 +954,8 @@ import dxCLASS_NAME, {
 import * as PropTypes from "prop-types";
 import { Component as BaseComponent, IHtmlOptions } from "BASE_COMPONENT_PATH";
 
-interface ICLASS_NAMEOptions extends IOptions, IHtmlOptions {
-}
+type ICLASS_NAMEOptions = React.PropsWithChildren<IOptions & IHtmlOptions & {
+}>
 
 class CLASS_NAME extends BaseComponent<ICLASS_NAMEOptions> {
 
@@ -1006,8 +1006,8 @@ import dxCLASS_NAME, {
 import * as PropTypes from "prop-types";
 import { Component as BaseComponent, IHtmlOptions } from "BASE_COMPONENT_PATH";
 
-interface ICLASS_NAMEOptions extends IOptions, IHtmlOptions {
-}
+type ICLASS_NAMEOptions = React.PropsWithChildren<IOptions & IHtmlOptions & {
+}>
 
 class CLASS_NAME extends BaseComponent<ICLASS_NAMEOptions> {
 
@@ -1057,8 +1057,8 @@ import dxCLASS_NAME, {
 import * as PropTypes from "prop-types";
 import { Component as BaseComponent, IHtmlOptions } from "BASE_COMPONENT_PATH";
 
-interface ICLASS_NAMEOptions extends IOptions, IHtmlOptions {
-}
+type ICLASS_NAMEOptions = React.PropsWithChildren<IOptions & IHtmlOptions & {
+}>
 
 class CLASS_NAME extends BaseComponent<ICLASS_NAMEOptions> {
 
@@ -1124,8 +1124,8 @@ import dxCLASS_NAME, {
 
 import { Component as BaseComponent, IHtmlOptions } from "BASE_COMPONENT_PATH";
 
-interface ICLASS_NAMEOptions extends IOptions, IHtmlOptions {
-}
+type ICLASS_NAMEOptions = React.PropsWithChildren<IOptions & IHtmlOptions & {
+}>
 
 class CLASS_NAME extends BaseComponent<ICLASS_NAMEOptions> {
 
@@ -1172,8 +1172,8 @@ import dxCLASS_NAME, {
 import { Component as BaseComponent, IHtmlOptions } from "BASE_COMPONENT_PATH";
 import NestedOption from "CONFIG_COMPONENT_PATH";
 
-interface ICLASS_NAMEOptions extends IOptions, IHtmlOptions {
-}
+type ICLASS_NAMEOptions = React.PropsWithChildren<IOptions & IHtmlOptions & {
+}>
 
 class CLASS_NAME extends BaseComponent<ICLASS_NAMEOptions> {
 
@@ -1187,8 +1187,8 @@ class CLASS_NAME extends BaseComponent<ICLASS_NAMEOptions> {
 
 // owners:
 // CLASS_NAME
-interface IOpt_1_ComponentProps {
-}
+type IOpt_1_ComponentProps = React.PropsWithChildren<{
+}>
 class Opt_1_Component extends NestedOption<IOpt_1_ComponentProps> {
   public static OptionName = "opt_1";
   public static ExpectedChildren = {
diff --git a/packages/devextreme-react/src/core/__tests__/component.test.tsx b/packages/devextreme-react/src/core/__tests__/component.test.tsx
index 353453e..75cd4dd 100644
--- a/packages/devextreme-react/src/core/__tests__/component.test.tsx
+++ b/packages/devextreme-react/src/core/__tests__/component.test.tsx
@@ -55,6 +55,27 @@ describe('rendering', () => {
     expect(templatesRendererRenderFn).toHaveBeenCalledTimes(1);
   });
 
+  it('renders component with children correctly after unmount', () => {
+    const component = (
+      <TestComponent>
+        <div>Test</div>
+      </TestComponent>
+    );
+    const { container, unmount, rerender } = render(
+      component,
+    );
+
+    unmount();
+    rerender(component);
+
+    expect(container.children.length).toBe(1);
+
+    const content = container.firstChild as HTMLElement;
+    expect(content.tagName.toLowerCase()).toBe('div');
+    expect(content.children.length).toBe(1);
+    expect(content.children[0].tagName.toLowerCase()).toBe('div');
+  });
+
   it('renders portal component without children correctly', () => {
     const createPortalFn = jest.spyOn(ReactDOM, 'createPortal');
     const templatesRendererRenderFn = jest.spyOn(TemplatesRenderer.prototype, 'render');
diff --git a/packages/devextreme-react/src/core/__tests__/templates-store.test.tsx b/packages/devextreme-react/src/core/__tests__/templates-store.test.tsx
index ba27d1c..7651c83 100644
--- a/packages/devextreme-react/src/core/__tests__/templates-store.test.tsx
+++ b/packages/devextreme-react/src/core/__tests__/templates-store.test.tsx
@@ -32,26 +32,26 @@ describe('templates-store', () => {
     expect(callback.mock.calls.length).toBe(2);
   });
 
-  it('removes', () => {
+  it('setDeferredRemove for remove', () => {
     const templatesStore = new TemplatesStore(jest.fn());
 
     templatesStore.add('1', jest.fn());
     templatesStore.add('2', jest.fn());
 
-    templatesStore.remove('1');
+    templatesStore.setDeferredRemove('1', true);
     expect(templatesStore.renderWrappers().length).toBe(1);
 
-    templatesStore.remove('2');
+    templatesStore.setDeferredRemove('2', true);
     expect(templatesStore.renderWrappers().length).toBe(0);
   });
 
-  it('removes not existing template correctly', () => {
+  it('setDeferredRemove not existing template correctly', () => {
     const templatesStore = new TemplatesStore(jest.fn());
 
     templatesStore.add('1', jest.fn());
 
-    templatesStore.remove('2');
-    templatesStore.remove('3');
+    templatesStore.setDeferredRemove('2', true);
+    templatesStore.setDeferredRemove('3', true);
     expect(templatesStore.renderWrappers().length).toBe(1);
   });
 
@@ -64,12 +64,22 @@ describe('templates-store', () => {
 
     expect(callback.mock.calls.length).toBe(2);
 
-    templatesStore.remove('1');
-    templatesStore.remove('2');
+    templatesStore.setDeferredRemove('1', true);
+    templatesStore.setDeferredRemove('2', true);
 
     expect(callback.mock.calls.length).toBe(2);
   });
 
+  it('toggle deferred remove shouldnt remove template (Unmount, Mount scenarion)', () => {
+    const templatesStore = new TemplatesStore(jest.fn());
+
+    templatesStore.add('1', jest.fn());
+
+    templatesStore.setDeferredRemove('1', true);
+    templatesStore.setDeferredRemove('1', false);
+    expect(templatesStore.renderWrappers().length).toBe(1);
+  });
+
   it('lists renderers execution results', () => {
     const templatesStore = new TemplatesStore(jest.fn());
     const renderer1 = jest.fn(() => 1);