Skip to content

Commit 7ba2721

Browse files
Merge remote-tracking branch 'origin/master' into chainRefs
2 parents ce1a5f2 + 8302980 commit 7ba2721

7 files changed

+297
-73
lines changed

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
## [5.4.0](https://github.com/cleandart/react-dart/compare/5.3.0...5.4.0)
2+
3+
__New Features__
4+
- [#244] Add support for [HTML Composition events](https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent)
5+
- [#263] Add support for [`SyntheticEvent.persist()`](https://reactjs.org/docs/events.html#event-pooling)
6+
7+
__Fixes / Updates__
8+
- [#261] Stop errors thrown within the call stack of `Component.render()` from being swallowed
9+
- [#256] Documentation updates _(thanks @barriehadfield !!!)_
10+
11+
__JS Dependency Updates__
12+
- [#255] Bump acorn from `6.4.0` to `6.4.1`
13+
- [#260] Bump lodash from `4.17.15` to `4.17.19`
14+
15+
116
## [5.3.0](https://github.com/cleandart/react-dart/compare/5.2.1...5.3.0)
217

318
- Unpin the react-redux JS dependency to allow versions `7.1.1` and higher.

lib/react.dart

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1451,6 +1451,61 @@ class SyntheticEvent {
14511451
/// See: <https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation>
14521452
final dynamic stopPropagation;
14531453

1454+
/// For use by react-dart internals only.
1455+
@protected
1456+
void Function() $$jsPersistDoNotSetThisOrYouWillBeFired;
1457+
bool _isPersistent = false;
1458+
1459+
/// Whether the event instance has been removed from the ReactJS event pool.
1460+
///
1461+
/// > See: [persist]
1462+
bool get isPersistent => _isPersistent;
1463+
1464+
/// Call this method on the current event instance if you want to access the event properties in an asynchronous way.
1465+
///
1466+
/// This will remove the synthetic event from the event pool and allow references
1467+
/// to the event to be retained by user code.
1468+
///
1469+
/// For example, `setState` callbacks are fired after a component updates as a result of the
1470+
/// new state being passed in - and since component updates are not guaranteed to by synchronous, any
1471+
/// reference to a `SyntheticEvent` within that callback could have been recycled by ReactJS internals.
1472+
///
1473+
/// You can use `persist()` to ensure access to the event properties within the callback as shown in
1474+
/// the second example below.
1475+
///
1476+
/// __Without persist()__
1477+
/// ```dart
1478+
/// void handleClick(SyntheticMouseEvent event) {
1479+
/// print(event.type); // => "click"
1480+
///
1481+
/// setState({}, () {
1482+
/// print(event.type); // => null
1483+
/// print(event.isPersistent); => false
1484+
/// });
1485+
/// }
1486+
/// ```
1487+
///
1488+
/// __With persist()__
1489+
/// ```dart
1490+
/// void handleClick(SyntheticMouseEvent event) {
1491+
/// print(event.type); // => "click"
1492+
/// event.persist();
1493+
///
1494+
/// setState({}, () {
1495+
/// print(event.type); // => "click"
1496+
/// print(event.isPersistent); => true
1497+
/// });
1498+
/// }
1499+
/// ```
1500+
///
1501+
/// See: <https://reactjs.org/docs/events.html#event-pooling>
1502+
void persist() {
1503+
if ($$jsPersistDoNotSetThisOrYouWillBeFired != null) {
1504+
_isPersistent = true;
1505+
$$jsPersistDoNotSetThisOrYouWillBeFired();
1506+
}
1507+
}
1508+
14541509
/// Indicates which phase of the [Event] flow is currently being evaluated.
14551510
///
14561511
/// Possible values:

lib/react_client.dart

Lines changed: 119 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -630,32 +630,37 @@ ReactDartComponentFactoryProxy _registerComponent(
630630
ComponentFactory componentFactory, [
631631
Iterable<String> skipMethods = const ['getDerivedStateFromError', 'componentDidCatch'],
632632
]) {
633-
var componentInstance = componentFactory();
634-
635-
if (componentInstance is Component2) {
636-
return _registerComponent2(componentFactory, skipMethods: skipMethods);
637-
}
633+
try {
634+
var componentInstance = componentFactory();
638635

639-
var componentStatics = new ComponentStatics(componentFactory);
636+
if (componentInstance is Component2) {
637+
return _registerComponent2(componentFactory, skipMethods: skipMethods);
638+
}
640639

641-
var jsConfig = new JsComponentConfig(
642-
childContextKeys: componentInstance.childContextKeys,
643-
contextKeys: componentInstance.contextKeys,
644-
);
640+
var componentStatics = new ComponentStatics(componentFactory);
645641

646-
/// Create the JS [`ReactClass` component class](https://facebook.github.io/react/docs/top-level-api.html#react.createclass)
647-
/// with custom JS lifecycle methods.
648-
var reactComponentClass = createReactDartComponentClass(_dartInteropStatics, componentStatics, jsConfig)
649-
// ignore: invalid_use_of_protected_member
650-
..dartComponentVersion = ReactDartComponentVersion.component
651-
..displayName = componentFactory().displayName;
652-
653-
// Cache default props and store them on the ReactClass so they can be used
654-
// by ReactDartComponentFactoryProxy and externally.
655-
final Map defaultProps = new Map.unmodifiable(componentInstance.getDefaultProps());
656-
reactComponentClass.dartDefaultProps = defaultProps;
642+
var jsConfig = new JsComponentConfig(
643+
childContextKeys: componentInstance.childContextKeys,
644+
contextKeys: componentInstance.contextKeys,
645+
);
657646

658-
return new ReactDartComponentFactoryProxy(reactComponentClass);
647+
/// Create the JS [`ReactClass` component class](https://facebook.github.io/react/docs/top-level-api.html#react.createclass)
648+
/// with custom JS lifecycle methods.
649+
var reactComponentClass = createReactDartComponentClass(_dartInteropStatics, componentStatics, jsConfig)
650+
// ignore: invalid_use_of_protected_member
651+
..dartComponentVersion = ReactDartComponentVersion.component
652+
..displayName = componentFactory().displayName;
653+
654+
// Cache default props and store them on the ReactClass so they can be used
655+
// by ReactDartComponentFactoryProxy and externally.
656+
final Map defaultProps = new Map.unmodifiable(componentInstance.getDefaultProps());
657+
reactComponentClass.dartDefaultProps = defaultProps;
658+
659+
return new ReactDartComponentFactoryProxy(reactComponentClass);
660+
} catch (e, stack) {
661+
print('Error when registering Component: $e\n$stack');
662+
rethrow;
663+
}
659664
}
660665

661666
/// Creates ReactJS [ReactElement] instances for `JSContext` components.
@@ -776,39 +781,58 @@ ReactDartComponentFactoryProxy2 _registerComponent2(
776781
Iterable<String> skipMethods = const ['getDerivedStateFromError', 'componentDidCatch'],
777782
Component2BridgeFactory bridgeFactory,
778783
}) {
779-
bridgeFactory ??= Component2BridgeImpl.bridgeFactory;
780-
781-
final componentInstance = componentFactory();
782-
final componentStatics = new ComponentStatics2(
783-
componentFactory: componentFactory,
784-
instanceForStaticMethods: componentInstance,
785-
bridgeFactory: bridgeFactory,
786-
);
787-
final filteredSkipMethods = _filterSkipMethods(skipMethods);
788-
789-
// Cache default props and store them on the ReactClass so they can be used
790-
// by ReactDartComponentFactoryProxy and externally.
791-
final JsBackedMap defaultProps = new JsBackedMap.from(componentInstance.defaultProps);
792-
793-
final JsMap jsPropTypes =
794-
bridgeFactory(componentInstance).jsifyPropTypes(componentInstance, componentInstance.propTypes);
795-
796-
var jsConfig2 = new JsComponentConfig2(
797-
defaultProps: defaultProps.jsObject,
798-
contextType: componentInstance.contextType?.jsThis,
799-
skipMethods: filteredSkipMethods,
800-
propTypes: jsPropTypes,
801-
);
802-
803-
/// Create the JS [`ReactClass` component class](https://facebook.github.io/react/docs/top-level-api.html#react.createclass)
804-
/// with custom JS lifecycle methods.
805-
var reactComponentClass =
806-
createReactDartComponentClass2(_ReactDartInteropStatics2.staticsForJs, componentStatics, jsConfig2)
807-
..displayName = componentInstance.displayName;
808-
// ignore: invalid_use_of_protected_member
809-
reactComponentClass.dartComponentVersion = ReactDartComponentVersion.component2;
810-
811-
return new ReactDartComponentFactoryProxy2(reactComponentClass);
784+
bool errorPrinted = false;
785+
try {
786+
bridgeFactory ??= Component2BridgeImpl.bridgeFactory;
787+
788+
final componentInstance = componentFactory();
789+
final componentStatics = new ComponentStatics2(
790+
componentFactory: componentFactory,
791+
instanceForStaticMethods: componentInstance,
792+
bridgeFactory: bridgeFactory,
793+
);
794+
final filteredSkipMethods = _filterSkipMethods(skipMethods);
795+
796+
// Cache default props and store them on the ReactClass so they can be used
797+
// by ReactDartComponentFactoryProxy and externally.
798+
JsBackedMap defaultProps;
799+
try {
800+
defaultProps = JsBackedMap.from(componentInstance.defaultProps);
801+
} catch (e, stack) {
802+
print('Error when registering Component2 when getting defaultProps: $e\n$stack');
803+
errorPrinted = true;
804+
rethrow;
805+
}
806+
807+
JsMap jsPropTypes;
808+
try {
809+
jsPropTypes = bridgeFactory(componentInstance).jsifyPropTypes(componentInstance, componentInstance.propTypes);
810+
} catch (e, stack) {
811+
print('Error when registering Component2 when getting propTypes: $e\n$stack');
812+
errorPrinted = true;
813+
rethrow;
814+
}
815+
816+
var jsConfig2 = new JsComponentConfig2(
817+
defaultProps: defaultProps.jsObject,
818+
contextType: componentInstance.contextType?.jsThis,
819+
skipMethods: filteredSkipMethods,
820+
propTypes: jsPropTypes,
821+
);
822+
823+
/// Create the JS [`ReactClass` component class](https://facebook.github.io/react/docs/top-level-api.html#react.createclass)
824+
/// with custom JS lifecycle methods.
825+
var reactComponentClass =
826+
createReactDartComponentClass2(_ReactDartInteropStatics2.staticsForJs, componentStatics, jsConfig2)
827+
..displayName = componentInstance.displayName;
828+
// ignore: invalid_use_of_protected_member
829+
reactComponentClass.dartComponentVersion = ReactDartComponentVersion.component2;
830+
831+
return new ReactDartComponentFactoryProxy2(reactComponentClass);
832+
} catch (e, stack) {
833+
if (!errorPrinted) print('Error when registering Component2: $e\n$stack');
834+
rethrow;
835+
}
812836
}
813837

814838
/// Creates ReactJS [ReactElement] instances for DOM components.
@@ -939,8 +963,10 @@ _convertEventHandlers(Map args) {
939963

940964
/// Wrapper for [SyntheticEvent].
941965
SyntheticEvent syntheticEventFactory(events.SyntheticEvent e) {
942-
return new SyntheticEvent(e.bubbles, e.cancelable, e.currentTarget, e.defaultPrevented, () => e.preventDefault(),
943-
() => e.stopPropagation(), e.eventPhase, e.isTrusted, e.nativeEvent, e.target, e.timeStamp, e.type);
966+
return SyntheticEvent(e.bubbles, e.cancelable, e.currentTarget, e.defaultPrevented, () => e.preventDefault(),
967+
() => e.stopPropagation(), e.eventPhase, e.isTrusted, e.nativeEvent, e.target, e.timeStamp, e.type)
968+
// ignore: invalid_use_of_protected_member
969+
..$$jsPersistDoNotSetThisOrYouWillBeFired = () => e.persist();
944970
}
945971

946972
/// Wrapper for [SyntheticClipboardEvent].
@@ -958,7 +984,9 @@ SyntheticClipboardEvent syntheticClipboardEventFactory(events.SyntheticClipboard
958984
e.target,
959985
e.timeStamp,
960986
e.type,
961-
e.clipboardData);
987+
e.clipboardData)
988+
// ignore: invalid_use_of_protected_member
989+
..$$jsPersistDoNotSetThisOrYouWillBeFired = () => e.persist();
962990
}
963991

964992
/// Wrapper for [SyntheticKeyboardEvent].
@@ -986,7 +1014,9 @@ SyntheticKeyboardEvent syntheticKeyboardEventFactory(events.SyntheticKeyboardEve
9861014
e.keyCode,
9871015
e.metaKey,
9881016
e.repeat,
989-
e.shiftKey);
1017+
e.shiftKey)
1018+
// ignore: invalid_use_of_protected_member
1019+
..$$jsPersistDoNotSetThisOrYouWillBeFired = () => e.persist();
9901020
}
9911021

9921022
/// Wrapper for [SyntheticCompositionEvent].
@@ -1004,7 +1034,9 @@ SyntheticCompositionEvent syntheticCompositionEventFactory(events.SyntheticCompo
10041034
e.target,
10051035
e.timeStamp,
10061036
e.type,
1007-
e.data);
1037+
e.data)
1038+
// ignore: invalid_use_of_protected_member
1039+
..$$jsPersistDoNotSetThisOrYouWillBeFired = () => e.persist();
10081040
}
10091041

10101042
/// Wrapper for [SyntheticFocusEvent].
@@ -1022,13 +1054,17 @@ SyntheticFocusEvent syntheticFocusEventFactory(events.SyntheticFocusEvent e) {
10221054
e.target,
10231055
e.timeStamp,
10241056
e.type,
1025-
e.relatedTarget);
1057+
e.relatedTarget)
1058+
// ignore: invalid_use_of_protected_member
1059+
..$$jsPersistDoNotSetThisOrYouWillBeFired = () => e.persist();
10261060
}
10271061

10281062
/// Wrapper for [SyntheticFormEvent].
10291063
SyntheticFormEvent syntheticFormEventFactory(events.SyntheticFormEvent e) {
10301064
return new SyntheticFormEvent(e.bubbles, e.cancelable, e.currentTarget, e.defaultPrevented, () => e.preventDefault(),
1031-
() => e.stopPropagation(), e.eventPhase, e.isTrusted, e.nativeEvent, e.target, e.timeStamp, e.type);
1065+
() => e.stopPropagation(), e.eventPhase, e.isTrusted, e.nativeEvent, e.target, e.timeStamp, e.type)
1066+
// ignore: invalid_use_of_protected_member
1067+
..$$jsPersistDoNotSetThisOrYouWillBeFired = () => e.persist();
10321068
}
10331069

10341070
/// Wrapper for [SyntheticDataTransfer].
@@ -1127,7 +1163,9 @@ SyntheticPointerEvent syntheticPointerEventFactory(events.SyntheticPointerEvent
11271163
e.twist,
11281164
e.pointerType,
11291165
e.isPrimary,
1130-
);
1166+
)
1167+
// ignore: invalid_use_of_protected_member
1168+
..$$jsPersistDoNotSetThisOrYouWillBeFired = () => e.persist();
11311169
}
11321170

11331171
/// Wrapper for [SyntheticMouseEvent].
@@ -1160,7 +1198,9 @@ SyntheticMouseEvent syntheticMouseEventFactory(events.SyntheticMouseEvent e) {
11601198
e.screenX,
11611199
e.screenY,
11621200
e.shiftKey,
1163-
);
1201+
)
1202+
// ignore: invalid_use_of_protected_member
1203+
..$$jsPersistDoNotSetThisOrYouWillBeFired = () => e.persist();
11641204
}
11651205

11661206
/// Wrapper for [SyntheticTouchEvent].
@@ -1185,7 +1225,9 @@ SyntheticTouchEvent syntheticTouchEventFactory(events.SyntheticTouchEvent e) {
11851225
e.shiftKey,
11861226
e.targetTouches,
11871227
e.touches,
1188-
);
1228+
)
1229+
// ignore: invalid_use_of_protected_member
1230+
..$$jsPersistDoNotSetThisOrYouWillBeFired = () => e.persist();
11891231
}
11901232

11911233
/// Wrapper for [SyntheticTransitionEvent].
@@ -1206,7 +1248,9 @@ SyntheticTransitionEvent syntheticTransitionEventFactory(events.SyntheticTransit
12061248
e.propertyName,
12071249
e.elapsedTime,
12081250
e.pseudoElement,
1209-
);
1251+
)
1252+
// ignore: invalid_use_of_protected_member
1253+
..$$jsPersistDoNotSetThisOrYouWillBeFired = () => e.persist();
12101254
}
12111255

12121256
/// Wrapper for [SyntheticAnimationEvent].
@@ -1227,7 +1271,9 @@ SyntheticAnimationEvent syntheticAnimationEventFactory(events.SyntheticAnimation
12271271
e.animationName,
12281272
e.elapsedTime,
12291273
e.pseudoElement,
1230-
);
1274+
)
1275+
// ignore: invalid_use_of_protected_member
1276+
..$$jsPersistDoNotSetThisOrYouWillBeFired = () => e.persist();
12311277
}
12321278

12331279
/// Wrapper for [SyntheticUIEvent].
@@ -1247,7 +1293,9 @@ SyntheticUIEvent syntheticUIEventFactory(events.SyntheticUIEvent e) {
12471293
e.type,
12481294
e.detail,
12491295
e.view,
1250-
);
1296+
)
1297+
// ignore: invalid_use_of_protected_member
1298+
..$$jsPersistDoNotSetThisOrYouWillBeFired = () => e.persist();
12511299
}
12521300

12531301
/// Wrapper for [SyntheticWheelEvent].
@@ -1269,7 +1317,9 @@ SyntheticWheelEvent syntheticWheelEventFactory(events.SyntheticWheelEvent e) {
12691317
e.deltaMode,
12701318
e.deltaY,
12711319
e.deltaZ,
1272-
);
1320+
)
1321+
// ignore: invalid_use_of_protected_member
1322+
..$$jsPersistDoNotSetThisOrYouWillBeFired = () => e.persist();
12731323
}
12741324

12751325
dynamic _findDomNode(component) {

package-lock.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)