Skip to content

Commit c774253

Browse files
Merge remote-tracking branch 'origin/master' into react-18-rc-test
2 parents 9884640 + c02b780 commit c774253

25 files changed

+506
-118
lines changed

.github/workflows/dart_ci.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ jobs:
1515
strategy:
1616
fail-fast: false
1717
matrix:
18-
sdk: [2.13.4, stable, beta]
18+
# Can't run on `beta` (Dart 3) until we're fully null-safe.
19+
sdk: [2.18.7, stable]
1920
steps:
2021
- uses: actions/checkout@v2
2122
- uses: dart-lang/setup-dart@v1
@@ -35,7 +36,7 @@ jobs:
3536

3637
- name: Verify formatting
3738
run: dart format --output=none --line-length=120 --set-exit-if-changed .
38-
if: ${{ matrix.sdk == '2.13.4' }}
39+
if: ${{ matrix.sdk == '2.18.7' }}
3940

4041
- name: Analyze project source
4142
run: dart analyze

CHANGELOG.md

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,19 @@
1-
## [6.1.6](https://github.com/cleandart/react-dart/compare/6.1.5...6.1.6)##
1+
## [6.2.0](https://github.com/cleandart/react-dart/compare/6.1.8...6.2.0)
2+
- [#357] Export Suspense component
3+
- [#354] Raise analyzer minimum & unpin meta
4+
- [#355] Pin mockito 5
5+
- [#356] Unpin mockito, fix CI
6+
7+
## [6.1.8](https://github.com/cleandart/react-dart/compare/6.1.7...6.1.8)
8+
- [#350] Return `jsUndefined` instead of `null` when children prop is empty
9+
10+
## [6.1.7](https://github.com/cleandart/react-dart/compare/6.1.6...6.1.7)
11+
- [#344] Fix identityHashCode error when forwarding props containing JSX children
12+
13+
## [6.1.6](https://github.com/cleandart/react-dart/compare/6.1.5...6.1.6)
214
- [#336] Publish JS bundles to Workiva CDN
315

4-
## [6.1.5](https://github.com/cleandart/react-dart/compare/6.1.4...6.1.5)##
16+
## [6.1.5](https://github.com/cleandart/react-dart/compare/6.1.4...6.1.5)
517
- [#333] Remove old, unused file from bin/
618

719
## [6.1.4](https://github.com/cleandart/react-dart/compare/6.1.3...6.1.4)
@@ -51,16 +63,16 @@ The underlying `.js` files provided by this package are now ReactJS version `17.
5163
5264
## [5.7.1](https://github.com/cleandart/react-dart/compare/5.7.0...5.7.1)
5365

54-
- [#289] Update most deprecations that were slated for removal in v6.0.0 to be slated for removal in v7.0.0 instead. To keep the migration to v6.0.0 as easy as possible, only APIs that are known to be completely unused will be removed in v6.0.0. Therefore, most APIs that were marked for removal in v6.0.0 will remain until the v7.0.0 release. This PR updated deprecation annotations to reflect this.
55-
- [#287] Deprecate `SyntheticEvent.isFormEvent`. Because form events do not exist as their own type in ReactJS, this helper will be removed in v6.0.0. Instead, check for the expected [form event types](https://reactjs.org/docs/events.html#form-events).
66+
- [#289] Update most deprecations that were slated for removal in v6.0.0 to be slated for removal in v7.0.0 instead. To keep the migration to v6.0.0 as easy as possible, only APIs that are known to be completely unused will be removed in v6.0.0. Therefore, most APIs that were marked for removal in v6.0.0 will remain until the v7.0.0 release. This PR updated deprecation annotations to reflect this.
67+
- [#287] Deprecate `SyntheticEvent.isFormEvent`. Because form events do not exist as their own type in ReactJS, this helper will be removed in v6.0.0. Instead, check for the expected [form event types](https://reactjs.org/docs/events.html#form-events).
5668

5769
## [5.7.0](https://github.com/cleandart/react-dart/compare/5.6.1...5.7.0)
5870

59-
- [#282] Add `SyntheticEvent` helpers that eliminate the need to use synthetic event class constructors. Additionally, added utilities to assist in type checking events without manually using the `is` keyword.
71+
- [#282] Add `SyntheticEvent` helpers that eliminate the need to use synthetic event class constructors. Additionally, added utilities to assist in type checking events without manually using the `is` keyword.
6072

6173
## [5.6.1](https://github.com/cleandart/react-dart/compare/5.6.0...5.6.1)
6274

63-
- [#280] Update React dev JS files to include a [workaround](https://github.com/dart-lang/sdk/issues/43193) to a DDC bug when using Chrome 86+ (fixed in Dart 2.9.3)
75+
- [#280] Update React dev JS files to include a [workaround](https://github.com/dart-lang/sdk/issues/43193) to a DDC bug when using Chrome 86+ (fixed in Dart 2.9.3)
6476

6577
## [5.6.0](https://github.com/cleandart/react-dart/compare/5.5.1...5.6.0)
6678

@@ -81,8 +93,8 @@ __New Features__
8193

8294
- 🎉 🎉 🎉 __Support for function components, memo and hooks!!!__ 🎉 🎉 🎉
8395

84-
Sooooo much work from so many amazing people made this possible, but to summarize:
85-
96+
Sooooo much work from so many amazing people made this possible, but to summarize:
97+
8698
- [#221] Add support for function components
8799
- [#252] Add support for `memo` higher order component
88100
- Hooks, hooks, and more hooks!
@@ -98,41 +110,41 @@ __New Features__
98110
- [#246] useDebugValue
99111

100112
<p><br>It works like this...</p>
101-
113+
102114
Define the component:
103115
```dart
104116
import 'package:react/react.dart' as react;
105-
117+
106118
final SomeWidget = react.registerFunctionComponent(_SomeWidget, displayName: 'SomeWidget');
107-
119+
108120
_SomeWidget(Map props) {
109121
// You can use hooks in here, too!
110-
122+
111123
return react.div({}, [
112124
// Some children...
113125
]);
114126
}
115127
```
116-
128+
117129
Render the component _(exact same consumer API as a class-based component)_:
118130
```dart
119131
import 'package:react/react_dom.dart' as react_dom;
120132
import 'some_widget.dart'; // Where your component is defined
121-
133+
122134
main() {
123135
final root = react_dom.createRoot(querySelector('#idOfSomeNodeInTheDom'));
124136
final renderedWidget = SomeWidget({
125137
// put some props here
126138
}, [
127139
// put some children here!
128140
]);
129-
141+
130142
root.render(renderedWidget);
131143
}
132144
```
133-
145+
134146
> Check out all the [function component and hooks examples](https://github.com/cleandart/react-dart/blob/c9a1211d5d77a9e354b864e99ef8f52b60eeff85/example/test/function_component_test.dart) for more information!
135-
147+
136148
__Fixes / Updates__
137149
- [#253] Deprecate `setClientConfiguration`.
138150
- It is no longer necessary - and can be removed from your implementations
@@ -142,7 +154,7 @@ __Fixes / Updates__
142154
143155
__New Features__
144156
- [#244] Add support for [HTML Composition events](https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent)
145-
- [#263] Add support for [`SyntheticEvent.persist()`](https://reactjs.org/docs/events.html#event-pooling)
157+
- [#263] Add support for [`SyntheticEvent.persist()`](https://reactjs.org/docs/events.html#event-pooling)
146158
147159
__Fixes / Updates__
148160
- [#261] Stop errors thrown within the call stack of `Component.render()` from being swallowed

Dockerfile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
FROM google/dart:2.13
1+
FROM drydock-prod.workiva.net/workiva/dart2_base_image:1 as dart2
2+
3+
RUN dart --version
24

35
WORKDIR /build
46
COPY pubspec.yaml .

example/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Serving / Viewing React Dart Examples
22

3-
1. Run `pub get` in your terminal.
3+
1. Run `dart pub get` in your terminal.
44
2. Run `webdev serve example`.
55
2. Open Chrome.
66
3. Navigate to <http://localhost:8080>

example/index.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ <h2 class="display-5">Use Case / Test Examples</h2>
5858
<li>
5959
<a href="test/function_component_test.html"><code>Function Component</code> test</a>
6060
</li>
61+
<li>
62+
<a href="suspense/index.html"><code>Suspense</code> Example</a>
63+
</li>
6164
</ul>
6265

6366
</div>

example/suspense/index.html

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head lang="en">
4+
<meta charset="UTF-8">
5+
<title>Suspense - react-dart Examples</title>
6+
<style>
7+
#content {
8+
width: 100%;
9+
height: 100%;
10+
text-align: center;
11+
}
12+
</style>
13+
</head>
14+
<body>
15+
<em>See Dart source for more info</em>
16+
17+
<div id="content"></div>
18+
19+
<script src="packages/react/react.js"></script>
20+
<script src="packages/react/react_dom.js"></script>
21+
22+
<!-- Where the JS React components are declared. -->
23+
<script defer src="suspense.dart.js"></script>
24+
25+
</body>
26+
</html>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import 'package:react/hooks.dart';
2+
import 'package:react/react.dart' as react;
3+
4+
var SimpleComponent = react.registerFunctionComponent(SimpleFunctionComponent, displayName: 'simple');
5+
6+
SimpleFunctionComponent(Map props) {
7+
final count = useState(1);
8+
final evenOdd = useState('even');
9+
10+
useEffect(() {
11+
if (count.value % 2 == 0) {
12+
print('count changed to ' + count.value.toString());
13+
evenOdd.set('even');
14+
} else {
15+
print('count changed to ' + count.value.toString());
16+
evenOdd.set('odd');
17+
}
18+
return () {
19+
print('count is changing... do some cleanup if you need to');
20+
};
21+
22+
/// This dependency prevents the effect from running every time [evenOdd.value] changes.
23+
}, [count.value]);
24+
25+
return react.div({}, [
26+
react.button({'onClick': (_) => count.set(1), 'key': 'ust1'}, ['Reset']),
27+
react.button({'onClick': (_) => count.setWithUpdater((prev) => prev + 1), 'key': 'ust2'}, ['+']),
28+
react.br({'key': 'ust3'}),
29+
react.p({'key': 'ust4'}, ['${count.value} is ${evenOdd.value}']),
30+
]);
31+
}

example/suspense/suspense.dart

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
@JS()
2+
library js_components;
3+
4+
import 'dart:html';
5+
import 'dart:js_util';
6+
7+
import 'package:js/js.dart';
8+
import 'package:react/react.dart' as react;
9+
import 'package:react/react_client.dart';
10+
import 'package:react/react_client/react_interop.dart';
11+
import 'package:react/react_dom.dart' as react_dom;
12+
import 'package:react/src/js_interop_util.dart';
13+
import './simple_component.dart' deferred as simple;
14+
15+
@JS('React.lazy')
16+
external ReactClass jsLazy(Promise Function() factory);
17+
18+
// Only intended for testing purposes, Please do not copy/paste this into repo.
19+
// This will most likely be added to the PUBLIC api in the future,
20+
// but needs more testing and Typing decisions to be made first.
21+
ReactJsComponentFactoryProxy lazy(Future<ReactComponentFactoryProxy> factory()) => ReactJsComponentFactoryProxy(
22+
jsLazy(
23+
allowInterop(
24+
() => futureToPromise(
25+
// React.lazy only supports "default exports" from a module.
26+
// This `{default: yourExport}` workaround can be found in the React.lazy RFC comments.
27+
// See: https://github.com/reactjs/rfcs/pull/64#issuecomment-431507924
28+
(() async => jsify({'default': (await factory()).type}))(),
29+
),
30+
),
31+
),
32+
);
33+
34+
main() {
35+
var content = wrapper({});
36+
37+
react_dom.render(content, querySelector('#content'));
38+
}
39+
40+
final lazyComponent = lazy(() async {
41+
await simple.loadLibrary();
42+
await Future.delayed(Duration(seconds: 5));
43+
return simple.SimpleComponent;
44+
});
45+
46+
var wrapper = react.registerFunctionComponent(WrapperComponent, displayName: 'wrapper');
47+
48+
WrapperComponent(Map props) {
49+
return react.div({
50+
'id': 'lazy-wrapper'
51+
}, [
52+
react.Suspense({'fallback': 'Loading...'}, [lazyComponent({})])
53+
]);
54+
}

lib/react.dart

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,38 @@ typedef ReactDartFunctionComponentFactoryProxy FunctionComponentRegistrar(DartFu
6565
/// See: <https://reactjs.org/docs/fragments.html>
6666
var Fragment = ReactJsComponentFactoryProxy(React.Fragment);
6767

68+
/// [Suspense] lets you display a fallback UI until its children have finished loading.
69+
///
70+
/// Like [react.Fragment], [Suspense] does not render any visible UI.
71+
/// It lets you specify a loading indicator in case some components in
72+
/// the tree below it are not yet ready to render.
73+
/// [Suspense] currently works with:
74+
/// - Components that use React.lazy
75+
/// - (dynamic imports, not currently implemented in dart)
76+
///
77+
/// Example Usage:
78+
/// ```
79+
/// render() {
80+
/// return react.div({}, [
81+
/// Header({}),
82+
/// react.Suspense({'fallback': LoadingIndicator({})}, [
83+
/// LazyBodyComponent({}),
84+
/// NotALazyComponent({})
85+
/// ]),
86+
/// Footer({}),
87+
/// ]);
88+
/// }
89+
/// ```
90+
///
91+
/// In the above example, [Suspense] will display the `LoadingIndicator` until
92+
/// `LazyBodyComponent` is loaded. It will not display for `Header` or `Footer`.
93+
///
94+
/// However, any "lazy" descendant components in `LazyBodyComponent` and
95+
/// `NotALazyComponent` will trigger the closest ancestor [Suspense].
96+
///
97+
/// See: <https://react.dev/reference/react/Suspense>
98+
var Suspense = ReactJsComponentFactoryProxy(React.Suspense);
99+
68100
/// StrictMode is a tool for highlighting potential problems in an application.
69101
///
70102
/// StrictMode does not render any visible UI. It activates additional checks and warnings for its descendants.

lib/react_client/js_interop_helpers.dart

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,32 @@ import "package:js/js.dart";
1010

1111
import 'js_backed_map.dart';
1212

13-
/// Like [identityHashCode], but uses a different hash for JS objects to work around an issue where
14-
/// [identityHashCode] adds an unwanted `$identityHash` property on JS objects (https://github.com/dart-lang/sdk/issues/47595).
15-
int _jsObjectFriendlyIdentityHashCode(Object object) =>
16-
object is JsMap ? _jsObjectHashCode(object) : identityHashCode(object);
13+
/// Like [identityHashCode], but uses a different hash for JS objects to work around:
14+
/// - an issue where [identityHashCode] adds an unwanted `$identityHash` property on JS objects: https://github.com/dart-lang/sdk/issues/47595
15+
/// - an issue where [identityHashCode] throws for frozen objects: https://github.com/dart-lang/sdk/issues/36354
16+
int _jsObjectFriendlyIdentityHashCode(Object object) {
17+
// Try detecting JS objects so we don't add properties to them.
18+
// Workaround for https://github.com/dart-lang/sdk/issues/47595
19+
if (object is JsMap) return _anonymousJsObjectOrFrozenObjectHashCode(object);
1720

18-
/// A hashCode implementation for JS objects.
21+
// If this fails, then most likely the object is a frozen JS object, such as props object or a variadic JSX children Array.
22+
// Note that props objects are typically handled by the above is JsMap case, though.
23+
// Fall back to a safe implementation.
24+
try {
25+
return identityHashCode(object);
26+
} catch (_) {
27+
return _anonymousJsObjectOrFrozenObjectHashCode(object);
28+
}
29+
}
30+
31+
/// A hashCode implementation for anonymous JS objects or frozen objects.
1932
///
2033
/// Even though the current implementation of returning the same hash code for all values is low-quality
2134
/// since all JS objects will collide, it is valid since it always returns the same value for the same object.
2235
///
23-
/// We also don't expect many JS objects to be passed into [jsifyAndAllowInterop], so the quality of this hash code
24-
/// is not of much concern.
25-
int _jsObjectHashCode(JsMap jsObject) => 0;
36+
/// We also don't expect many JS objects or frozen objects to be passed into [jsifyAndAllowInterop],
37+
/// so the quality of this hash code is not of much concern.
38+
int _anonymousJsObjectOrFrozenObjectHashCode(Object _) => 0;
2639

2740
// The following code is adapted from `package:js` in the dart-lang/sdk repo:
2841
// https://github.com/dart-lang/sdk/blob/2.2.0/sdk/lib/js_util/dart2js/js_util_dart2js.dart#L27

0 commit comments

Comments
 (0)