diff --git a/lib/src/component_declaration/flux_component.dart b/lib/src/component_declaration/flux_component.dart index 45c7d62f9..b5638ae87 100644 --- a/lib/src/component_declaration/flux_component.dart +++ b/lib/src/component_declaration/flux_component.dart @@ -175,6 +175,11 @@ abstract class _FluxComponentMixin implements Batche }); } + void componentDidUpdate(Map prevProps, Map prevState) { + // Let BatchedRedraws know that this component has redrawn in the current batch + didBatchRedraw = true; + } + void componentWillUnmount() { // Ensure that unmounted components don't batch render shouldBatchRedraw = false; diff --git a/pubspec.yaml b/pubspec.yaml index 6af58bd78..f7e1c7299 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,7 +17,7 @@ dependencies: source_span: "^1.4.0" transformer_utils: "^0.1.1" w_common: "^1.8.0" - w_flux: "^2.7.1" + w_flux: "^2.9.0" platform_detect: "^1.3.2" quiver: ">=0.21.4 <0.26.0" dev_dependencies: diff --git a/test/over_react/component_declaration/flux_component_test.dart b/test/over_react/component_declaration/flux_component_test.dart index ee6e3b0bb..257271e6d 100644 --- a/test/over_react/component_declaration/flux_component_test.dart +++ b/test/over_react/component_declaration/flux_component_test.dart @@ -13,6 +13,7 @@ import '../../test_util/test_util.dart'; part 'flux_component_test/default.dart'; part 'flux_component_test/handler_precedence.dart'; +part 'flux_component_test/nested.dart'; part 'flux_component_test/redraw_on.dart'; part 'flux_component_test/store_handlers.dart'; @@ -164,6 +165,51 @@ void main() { await nextTick(); expect(component.numberOfRedraws, equals(0)); }); + + test('only redraws once in response to a store trigger combined with an ancestor rerendering', () async { + var store = new Store(); + + TestNestedComponent nested0; + TestNestedComponent nested1; + TestNestedComponent nested2; + + render( + (TestNested() + ..store = store + ..ref = (ref) { nested0 = ref; } + )( + (TestNested() + ..store = store + ..ref = (ref) { nested1 = ref; } + )( + (TestNested() + ..store = store + ..ref = (ref) { nested2 = ref; } + )() + ) + ) + ); + expect(nested0.renderCount, 1, reason: 'setup check: initial render'); + expect(nested1.renderCount, 1, reason: 'setup check: initial render'); + expect(nested2.renderCount, 1, reason: 'setup check: initial render'); + + nested0.redraw(); + await nextTick(); + + expect(nested0.renderCount, 2, reason: 'setup check: components should rerender their children'); + expect(nested1.renderCount, 2, reason: 'setup check: components should rerender their children'); + expect(nested2.renderCount, 2, reason: 'setup check: components should rerender their children'); + + store.trigger(); + // Two async gaps just to be safe, since we're + // asserting that additional redraws don't happen. + await nextTick(); + await nextTick(); + + expect(nested0.renderCount, 3, reason: 'should have rerendered once in response to the store triggering'); + expect(nested1.renderCount, 3, reason: 'should have rerendered once in response to the store triggering'); + expect(nested2.renderCount, 3, reason: 'should have rerendered once in response to the store triggering'); + }); }); } diff --git a/test/over_react/component_declaration/flux_component_test/nested.dart b/test/over_react/component_declaration/flux_component_test/nested.dart new file mode 100644 index 000000000..4e9ba468f --- /dev/null +++ b/test/over_react/component_declaration/flux_component_test/nested.dart @@ -0,0 +1,27 @@ +part of over_react.component_declaration.flux_component_test; + +@Factory() +UiFactory TestNested; + +@Props() +class TestNestedProps extends FluxUiProps {} + +@Component() +class TestNestedComponent extends FluxUiComponent { + int renderCount = 0; + + @override + render() { + renderCount++; + + var keyCounter = 0; + var newChildren = props.children.map((child) { + // The keys are consistent across rerenders, so they aren't remounted, + // but cloning the element is necessary for react to consider it changed, + // since it returns new ReactElement instances. + return cloneElement(child, domProps()..key = keyCounter++); + }).toList(); + + return Dom.div()(newChildren); + } +}