Repository: DevExpress/devextreme-react
Pull request #155 (2018-07-12 14:56:37)

Fix updating of collection nested components 


Patch:
diff --git a/src/core/component.ts b/src/core/component.ts
index ca2376f..ff51614 100644
--- a/src/core/component.ts
+++ b/src/core/component.ts
@@ -60,7 +60,7 @@ abstract class ComponentBase<P> extends React.PureComponent<P, IState> {
         "div",
         { ref: (element: any) => this._element = element }
       ];
-
+      this._optionsManager.resetNestedElements();
       let nestedTemplates: Record<string, any> = {};
       React.Children.forEach(this.props.children, (child: React.ReactElement<any>) => {
         nestedTemplates = {
diff --git a/src/core/options-manager.ts b/src/core/options-manager.ts
index 2f59c50..f099b4b 100644
--- a/src/core/options-manager.ts
+++ b/src/core/options-manager.ts
@@ -46,6 +46,12 @@ class OptionsManager {
         this.processChangedValues = this.processChangedValues.bind(this);
     }
 
+    public resetNestedElements() {
+        Object.keys(this._nestedOptions).forEach((optionName) => {
+            this._nestedOptions[optionName].elementEntries.length = 0;
+        });
+    }
+
     public setInstance(instance: any) {
         this._instance = instance;
     }
@@ -144,7 +150,7 @@ class OptionsManager {
         Object.keys(optionsCollection).forEach((key) => {
             const nestedOption = optionsCollection[key];
             let templates = {};
-            const options = nestedOption.elementEntries.map((e) => {
+            const options = nestedOption.elementEntries.map((e, index) => {
                 const props = separateProps(e.element.props,
                     nestedOption.defaults,
                     nestedOption.templates);
@@ -154,8 +160,7 @@ class OptionsManager {
                     nestedOptions: {},
                     templateProps: nestedOption.templates,
                     stateUpdater,
-                    propsGetter: (prop) =>
-                      nestedOption.elementEntries[nestedOption.elementEntries.length - 1].element.props[prop]
+                    propsGetter: (prop) => nestedOption.elementEntries[index].element.props[prop]
                 });
 
                 templates = {
@@ -199,9 +204,18 @@ class OptionsManager {
         const nestedOptionsCollection: Record<string, INestedOptionDescr> = {};
         const optionName = nestedOptionClass.type.OptionName;
 
-        let optionFullName = nestedOptionClass.type.OptionName;
+        const entry = ensureNestedOption(
+            optionName,
+            owningCollection,
+            nestedOptionClass.type.DefaultsProps,
+            nestedOptionClass.type.TemplateProps,
+            nestedOptionClass.type.IsCollectionItem
+        );
+
+        const index = nestedOptionClass.type.IsCollectionItem ? `[${entry.elementEntries.length}]` : "";
+        let optionFullName = `${optionName}${index}`;
         if (ownerFullName) {
-            optionFullName = `${ownerFullName}.${optionName}`;
+            optionFullName = `${ownerFullName}.${optionFullName}`;
         }
 
         const optionComponent = createOptionComponent(
@@ -214,7 +228,7 @@ class OptionsManager {
                 updateFunc: (newProps, prevProps) => {
                     const newOptions = separateProps(newProps,
                         nestedOptionClass.type.DefaultsProps,
-                        nestedOptionClass.type.TemplateProps || []).options;
+                        nestedOptionClass.type.TemplateProps).options;
                     this.processChangedValues(
                         addPrefixToKeys(newOptions, optionFullName + "."),
                         addPrefixToKeys(prevProps, optionFullName + ".")
@@ -222,13 +236,6 @@ class OptionsManager {
                 }
             }
         );
-        const entry = ensureNestedOption(
-            optionName,
-            owningCollection,
-            nestedOptionClass.type.DefaultsProps,
-            nestedOptionClass.type.TemplateProps,
-            nestedOptionClass.type.IsCollectionItem
-        );
 
         entry.elementEntries.push({
             element,


Test patch:
diff --git a/src/core/__tests__/nested-option.test.tsx b/src/core/__tests__/nested-option.test.tsx
index 41f5820..e272cec 100644
--- a/src/core/__tests__/nested-option.test.tsx
+++ b/src/core/__tests__/nested-option.test.tsx
@@ -144,6 +144,26 @@ describe("nested option", () => {
         });
     });
 
+    it("is pulled as a collection item after update", () => {
+        const component = mount(
+            <TestComponent>
+                <CollectionNestedComponent key={1} c={123} d="abc" />
+                <CollectionNestedComponent key={2} c={456} />
+                <CollectionNestedComponent key={3} d="def" />
+            </TestComponent>
+        );
+        component.setProps({
+            children: [
+                <CollectionNestedComponent key={1} c={123} d="abc" />,
+                <CollectionNestedComponent key={2} c={999} />,
+                <CollectionNestedComponent key={3} d="def" />
+            ]});
+        jest.runAllTimers();
+
+        expect(Widget.option.mock.calls.length).toBe(1);
+        expect(Widget.option.mock.calls[0]).toEqual(["itemOptions[1].c", 999]);
+    });
+
     it("is pulled after update", () => {
 
         const component = shallow(
@@ -308,6 +328,31 @@ describe("nested sub-option", () => {
         });
     });
 
+    it("is pulled as a collection item after update inside another option", () => {
+        const component = mount(
+            <TestComponent>
+                <NestedComponent a={123} >
+                    <CollectionSubNestedComponent key={1} c={123} d="abc" />
+                    <CollectionSubNestedComponent key={2} c={456} />
+                    <CollectionSubNestedComponent key={3} d="def" />
+                </NestedComponent>
+            </TestComponent>
+        );
+        component.setProps({
+            children: (
+                <NestedComponent a={123} >
+                    <CollectionSubNestedComponent key={1} c={123} d="abc" />
+                    <CollectionSubNestedComponent key={2} c={999} />
+                    <CollectionSubNestedComponent key={3} d="def" />
+                </NestedComponent>
+            )
+        });
+        jest.runAllTimers();
+
+        expect(Widget.option.mock.calls.length).toBe(1);
+        expect(Widget.option.mock.calls[0]).toEqual(["option.subItemsOptions[1].c", 999]);
+    });
+
     it("is pulled after update", () => {
 
         const component = shallow(