diff --git a/lib/src/util/memo.dart b/lib/src/util/memo.dart index 449e588ef..c0051a2a5 100644 --- a/lib/src/util/memo.dart +++ b/lib/src/util/memo.dart @@ -15,6 +15,7 @@ library over_react.memo; import 'package:over_react/src/component_declaration/component_type_checking.dart'; +import 'package:over_react/src/util/equality.dart'; import 'package:react/react_client/react_interop.dart' as react_interop; import 'package:react/react_client.dart'; import 'package:over_react/component_base.dart'; @@ -32,25 +33,25 @@ import 'package:over_react/component_base.dart'; /// ```dart /// import 'package:over_react/over_react.dart'; /// -/// UiFactory MemoExample = memo(uiFunction( +/// UiFactory MemoExample = memo(uiFunction( /// (props) { /// // render using props /// }, -/// UiFactoryConfig(displayName: 'MemoExample'), +/// $MemoExampleConfig, // ignore: undefined_identifier /// )); /// ``` /// /// `memo` only affects props changes. If your function component wrapped in `memo` has a /// `useState` or `useContext` Hook in its implementation, it will still rerender when `state` or `context` change. /// -/// By default it will only shallowly compare complex objects in the props map. +/// By default it will only shallowly compare complex objects in the props map using [propsOrStateMapsEqual]. /// If you want control over the comparison, you can also provide a custom comparison /// function to the [areEqual] argument as shown in the example below. /// /// ```dart /// import 'package:over_react/over_react.dart'; /// -/// UiFactory MemoWithComparison = memo(uiFunction( +/// UiFactory MemoWithComparison = memo(uiFunction( /// (props) { /// // render using props /// }, @@ -80,7 +81,7 @@ UiFactory memo(UiFactory factory, hoc = react_interop.memo2(factory().componentFactory, areEqual: wrapProps); } else { - hoc = react_interop.memo2(factory().componentFactory); + hoc = react_interop.memo2(factory().componentFactory, areEqual: propsOrStateMapsEqual); } setComponentTypeMeta(hoc, diff --git a/test/over_react/component/memo_test.dart b/test/over_react/component/memo_test.dart index dc6886104..4d9fbc2e7 100644 --- a/test/over_react/component/memo_test.dart +++ b/test/over_react/component/memo_test.dart @@ -25,6 +25,7 @@ part 'memo_test.over_react.g.dart'; int renderCount = 0; mixin FunctionCustomPropsProps on UiProps { int testProp; + Function() testFuncProp; } UiFactory FunctionCustomProps = uiFunction( @@ -84,6 +85,24 @@ main() { expect(renderCount, equals(1)); }); + // Asserts that propsOrStateMapsEqual is being used for the equality check + // when the consumer does not provide a custom `areEqual` parameter. + test('memoizes properly when a tear-off is passed as a function prop value', () { + renderCount = 0; + UiFactory FunctionCustomPropsMemo = memo(FunctionCustomProps); + void handleTestFunc() {} + void handleTestFunc2() {} + final testJacket = mount((FunctionCustomPropsMemo()..testFuncProp = handleTestFunc)()); + testJacket.rerender((FunctionCustomPropsMemo()..testFuncProp = handleTestFunc)()); + testJacket.rerender((FunctionCustomPropsMemo()..testFuncProp = handleTestFunc)()); + + expect(renderCount, equals(1), reason: 'An identical tear-off value should not result in a re-render'); + + testJacket.rerender((FunctionCustomPropsMemo()..testFuncProp = handleTestFunc2)()); + + expect(renderCount, equals(2), reason: 'A different tear-off value should result in a re-render'); + }); + test('memoizes based on areEqual parameter', () { renderCount = 0; UiFactory FunctionCustomPropsMemo = diff --git a/test/over_react/component/memo_test.over_react.g.dart b/test/over_react/component/memo_test.over_react.g.dart index a6cd017df..69b317a94 100644 --- a/test/over_react/component/memo_test.over_react.g.dart +++ b/test/over_react/component/memo_test.over_react.g.dart @@ -181,17 +181,30 @@ mixin $FunctionCustomPropsProps on FunctionCustomPropsProps { @override set testProp(int value) => props[_$key__testProp__FunctionCustomPropsProps] = value; + @override + Function() get testFuncProp => + props[_$key__testFuncProp__FunctionCustomPropsProps] ?? + null; // Add ` ?? null` to workaround DDC bug: ; + @override + set testFuncProp(Function() value) => + props[_$key__testFuncProp__FunctionCustomPropsProps] = value; /* GENERATED CONSTANTS */ static const PropDescriptor _$prop__testProp__FunctionCustomPropsProps = PropDescriptor(_$key__testProp__FunctionCustomPropsProps); + static const PropDescriptor _$prop__testFuncProp__FunctionCustomPropsProps = + PropDescriptor(_$key__testFuncProp__FunctionCustomPropsProps); static const String _$key__testProp__FunctionCustomPropsProps = 'FunctionCustomPropsProps.testProp'; + static const String _$key__testFuncProp__FunctionCustomPropsProps = + 'FunctionCustomPropsProps.testFuncProp'; static const List $props = [ - _$prop__testProp__FunctionCustomPropsProps + _$prop__testProp__FunctionCustomPropsProps, + _$prop__testFuncProp__FunctionCustomPropsProps ]; static const List $propKeys = [ - _$key__testProp__FunctionCustomPropsProps + _$key__testProp__FunctionCustomPropsProps, + _$key__testFuncProp__FunctionCustomPropsProps ]; }