-
Notifications
You must be signed in to change notification settings - Fork 67
Add forwardRef2/memo2 which fixes jsification of Dart props #275
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
b4cb55f
c0fb93e
7b8fb0e
6256901
9f1aade
02a9aea
8468f54
0617c3a
d3036fd
54b21e9
490d7e5
c65febe
66e3bed
b202ca0
3dacc70
e9e62e9
ff46864
4a0cbfb
fdfbd53
b344b1b
9e70ee0
fda51a4
ccc1ad8
d2d04d2
874ccd6
450ecf1
9bc982d
4a0b4e3
f1ec940
e200d48
70ebf83
8d52aa7
fe266b9
be896a7
1d3fea5
3bbdff7
5807944
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,7 +12,10 @@ cache: | |
|
|
||
| before_script: | ||
| - dartanalyzer . | ||
| - dartfmt --line-length=120 --dry-run --set-exit-if-changed . | ||
| - | | ||
| if [ "$TRAVIS_DART_VERSION" != "dev" ]; then | ||
| dartfmt --line-length=120 --dry-run --set-exit-if-changed . | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This fixes failures we were seeing in the Dart dev builds due to the dartfmt being different |
||
| fi | ||
| - pub run dependency_validator -i build_runner,build_test,build_web_compilers | ||
|
|
||
| script: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,10 +14,11 @@ import 'package:react/src/context.dart'; | |
| import 'package:react/src/ddc_emulated_function_name_bug.dart' as ddc_emulated_function_name_bug; | ||
| import 'package:react/src/js_interop_util.dart'; | ||
| import 'package:react/src/typedefs.dart'; | ||
| import 'package:react/src/react_client/event_factory.dart'; | ||
| import 'package:react/src/react_client/event_prop_key_to_event_factory.dart'; | ||
| import 'package:react/src/react_client/factory_util.dart'; | ||
|
|
||
| export 'package:react/src/react_client/factory_util.dart' show unconvertJsEventHandler; | ||
|
|
||
| /// Prepares [children] to be passed to the ReactJS [React.createElement] and | ||
| /// the Dart [react.Component]. | ||
| /// | ||
|
|
@@ -36,20 +37,6 @@ dynamic listifyChildren(dynamic children) { | |
| } | ||
| } | ||
|
|
||
| /// Returns the original Dart handler function that, within [_convertEventHandlers], | ||
| /// was converted/wrapped into the function [jsConvertedEventHandler] to be passed to the JS. | ||
| /// | ||
| /// Returns `null` if [jsConvertedEventHandler] is `null`. | ||
| /// | ||
| /// Returns `null` if [jsConvertedEventHandler] does not represent such a function | ||
| /// | ||
| /// Useful for chaining event handlers on DOM or JS composite [ReactElement]s. | ||
| Function unconvertJsEventHandler(Function jsConvertedEventHandler) { | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I moved this into factory_util.dart so that it would live next to event conversion logic. I did this when I had similar unconversion logic for refs, but ended up not needing that new logic. Instead of moving |
||
| if (jsConvertedEventHandler == null) return null; | ||
|
|
||
| return originalEventHandlers[jsConvertedEventHandler]; | ||
| } | ||
|
|
||
| /// Returns the props for a [ReactElement] or composite [ReactComponent] [instance], | ||
| /// shallow-converted to a Dart Map for convenience. | ||
| /// | ||
|
|
@@ -172,10 +159,14 @@ class ReactDartComponentFactoryProxy<TComponent extends Component> extends React | |
| // would fail the `is CallbackRef<dynamic>` check. | ||
| // See https://github.com/dart-lang/sdk/issues/34593 for more information on arity checks. | ||
| if (ref is CallbackRef<Null>) { | ||
| interopProps.ref = allowInterop((ReactComponent instance) { | ||
| interopProps.ref = allowInterop((dynamic instance) { | ||
| // Call as dynamic to perform dynamic dispatch, since we can't cast to CallbackRef<dynamic>, | ||
| // and since calling with non-null values will fail at runtime due to the CallbackRef<Null> typing. | ||
| return (ref as dynamic)(instance?.dartComponent); | ||
| if (instance is ReactComponent && instance.dartComponent != null) { | ||
| return (ref as dynamic)(instance.dartComponent); | ||
| } | ||
|
|
||
| return (ref as dynamic)(instance); | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These changes update ReactDartComponentFactoryProxy's ref wrapping code to match that of ReactDartComponentFactoryProxy2, and were made to fix |
||
| }); | ||
| } else if (ref is Ref) { | ||
| interopProps.ref = ref.jsRef; | ||
|
|
@@ -369,34 +360,71 @@ class ReactDartFunctionComponentFactoryProxy extends ReactComponentFactoryProxy | |
|
|
||
| @override | ||
| JsFunctionComponent get type => reactFunction; | ||
| } | ||
|
|
||
| static String _getJsFunctionName(Function object) => | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use whitespace-agnostic diff for this section |
||
| getProperty(object, 'name') ?? getProperty(object, '\$static_name'); | ||
| /// A wrapper for a Dart component that's been wrapped by a JS component/HOC (e.g., [React.memo], [React.forwardRef]). | ||
| class ReactDartWrappedComponentFactoryProxy extends ReactComponentFactoryProxy with JsBackedMapComponentFactoryMixin { | ||
| /// The React JS component definition of this Function Component. | ||
| @override | ||
| final ReactClass type; | ||
|
|
||
| /// Creates a function component from the given [dartFunctionComponent] that can be used with React. | ||
| /// | ||
| /// [displayName] Sets the component name for debugging purposes. | ||
| /// | ||
| /// In DDC, this will be the [DartFunctionComponent] name, but in dart2js it will be null unless | ||
| /// overridden, since using runtimeType can lead to larger dart2js output. | ||
| /// | ||
| /// This will result in the dart2js name being `ReactDartComponent2` (the | ||
| /// name of the proxying JS component defined in _dart_helpers.js). | ||
| static JsFunctionComponent _wrapFunctionComponent(DartFunctionComponent dartFunctionComponent, {String displayName}) { | ||
| // dart2js uses null and undefined interchangeably, meaning returning `null` from dart | ||
| // may show up in js as `undefined`, ReactJS doesnt like that and expects a js `null` to be returned, | ||
| // and throws if it gets `undefined`. `jsNull` is an interop variable that holds a JS `null` value | ||
| // to force `null` as the return value if user returns a Dart `null`. | ||
| // See: https://github.com/dart-lang/sdk/issues/27485 | ||
| jsFunctionComponent(JsMap jsProps, [JsMap _legacyContext]) => | ||
| componentZone.run(() => dartFunctionComponent(JsBackedMap.backedBy(jsProps)) ?? jsNull); | ||
| JsFunctionComponent interopFunction = allowInterop(jsFunctionComponent); | ||
| if (displayName != null) { | ||
| // This is a work-around to display the correct name in the React DevTools. | ||
| defineProperty(interopFunction, 'name', jsify({'value': displayName})); | ||
| } | ||
| // ignore: invalid_use_of_protected_member | ||
| setProperty(interopFunction, 'dartComponentVersion', ReactDartComponentVersion.component2); | ||
| return interopFunction; | ||
| ReactDartWrappedComponentFactoryProxy(this.type); | ||
|
|
||
| ReactDartWrappedComponentFactoryProxy.forwardRef(DartForwardRefFunctionComponent dartFunctionComponent, | ||
| {String displayName}) | ||
| : this.type = _wrapForwardRefFunctionComponent(dartFunctionComponent, | ||
| displayName: displayName ?? _getJsFunctionName(dartFunctionComponent)); | ||
| } | ||
|
|
||
| String _getJsFunctionName(Function object) => getProperty(object, 'name') ?? getProperty(object, '\$static_name'); | ||
|
|
||
| /// Creates a function component from the given [dartFunctionComponent] that can be used with React. | ||
| /// | ||
| /// [displayName] Sets the component name for debugging purposes. | ||
| /// | ||
| /// In DDC, this will be the [DartFunctionComponent] name, but in dart2js it will be null unless | ||
| /// overridden, since using runtimeType can lead to larger dart2js output. | ||
| JsFunctionComponent _wrapFunctionComponent(DartFunctionComponent dartFunctionComponent, {String displayName}) { | ||
| // dart2js uses null and undefined interchangeably, meaning returning `null` from dart | ||
| // may show up in js as `undefined`, ReactJS doesnt like that and expects a js `null` to be returned, | ||
| // and throws if it gets `undefined`. `jsNull` is an interop variable that holds a JS `null` value | ||
| // to force `null` as the return value if user returns a Dart `null`. | ||
| // See: https://github.com/dart-lang/sdk/issues/27485 | ||
| jsFunctionComponent(JsMap jsProps, [JsMap _legacyContext]) => | ||
| componentZone.run(() => dartFunctionComponent(JsBackedMap.backedBy(jsProps)) ?? jsNull); | ||
| JsFunctionComponent interopFunction = allowInterop(jsFunctionComponent); | ||
| if (displayName != null) { | ||
| // This is a work-around to display the correct name in the React DevTools. | ||
| defineProperty(interopFunction, 'name', jsify({'value': displayName})); | ||
| } | ||
| // ignore: invalid_use_of_protected_member | ||
| setProperty(interopFunction, 'dartComponentVersion', ReactDartComponentVersion.component2); | ||
| return interopFunction; | ||
| } | ||
|
|
||
| /// Creates a function component from the given [dartFunctionComponent] that can be used with React. | ||
| /// | ||
| /// [displayName] Sets the component name for debugging purposes. | ||
| /// | ||
| /// In DDC, this will be the [DartFunctionComponent] name, but in dart2js it will be null unless | ||
| /// overridden, since using runtimeType can lead to larger dart2js output. | ||
| ReactClass _wrapForwardRefFunctionComponent(DartForwardRefFunctionComponent dartFunctionComponent, | ||
| {String displayName}) { | ||
| // dart2js uses null and undefined interchangeably, meaning returning `null` from dart | ||
| // may show up in js as `undefined`, ReactJS doesnt like that and expects a js `null` to be returned, | ||
| // and throws if it gets `undefined`. `jsNull` is an interop variable that holds a JS `null` value | ||
| // to force `null` as the return value if user returns a Dart `null`. | ||
| // See: https://github.com/dart-lang/sdk/issues/27485 | ||
| jsFunctionComponent(JsMap props, dynamic ref) => | ||
| componentZone.run(() => dartFunctionComponent(JsBackedMap.backedBy(props), ref) ?? jsNull); | ||
|
|
||
| final interopFunction = allowInterop(jsFunctionComponent); | ||
| if (displayName != null) { | ||
| // This is a work-around to display the correct name in the React DevTools. | ||
| defineProperty(interopFunction, 'name', jsify({'value': displayName})); | ||
| } | ||
| final jsForwardRefFunction = React.forwardRef(interopFunction); | ||
| // ignore: invalid_use_of_protected_member | ||
| setProperty(jsForwardRefFunction, 'dartComponentVersion', ReactDartComponentVersion.component2); | ||
| return jsForwardRefFunction; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.