Skip to content
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

FED-3114: Lazy #941

Merged
merged 33 commits into from
Oct 7, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
8e6fc92
first pass at lazy
kealjones-wk Sep 10, 2024
0ebd41f
uncomment out tests
kealjones-wk Sep 10, 2024
96ba2f8
try different dependency_overrides
kealjones-wk Sep 10, 2024
f5605c2
ugh
kealjones-wk Sep 10, 2024
131ceb9
add lazy tests
kealjones-wk Sep 12, 2024
6b6fd4c
get lazy prop conversion tests going
kealjones-wk Sep 16, 2024
4999c39
Merge branch 'master' into FED-3114-lazy
kealjones-wk Sep 16, 2024
6e112ff
remove unused code and imports
kealjones-wk Sep 16, 2024
41d389e
add a class component map props test
kealjones-wk Sep 16, 2024
3fd3865
undo named extensions
kealjones-wk Sep 16, 2024
d3b00c7
cleanup suspense example
kealjones-wk Sep 16, 2024
677b555
woops
kealjones-wk Sep 16, 2024
1e0b3a7
remove unused part
kealjones-wk Sep 16, 2024
9713881
fix suspense test lazy typing
kealjones-wk Sep 17, 2024
aed0afb
add doc comment
kealjones-wk Sep 17, 2024
e1704f4
fix lazy displyName config
kealjones-wk Sep 17, 2024
046c0f5
add ability to use Generic Props
kealjones-wk Sep 18, 2024
b5289ec
add snippets for lazy uiFunction and lazy uiForwardRef
kealjones-wk Sep 18, 2024
dbf8c32
address more feedback
kealjones-wk Oct 1, 2024
0edb580
apply greg's diff to fix analyzer error
kealjones-wk Oct 2, 2024
0cc1b79
rebuild
kealjones-wk Oct 2, 2024
3edb048
remove wrapper
kealjones-wk Oct 2, 2024
c9a17ea
move UiProps tests into lazy_test so that we can wrap correctly and t…
kealjones-wk Oct 7, 2024
2bb9479
add render success test
kealjones-wk Oct 7, 2024
8d81cf6
bahhh humbug
kealjones-wk Oct 7, 2024
ebf3afe
Fix bad import
kealjones-wk Oct 7, 2024
6c1f3f9
Reorganize tests, add coverage for null case, tweak expectations
greglittlefield-wf Oct 7, 2024
7ba044a
Fix lint
greglittlefield-wf Oct 7, 2024
ddfeb30
Merge pull request #954 from Workiva/FED-3114-lazy--test-organization
kealjones-wk Oct 7, 2024
86c0e50
prevent using null with lazy
kealjones-wk Oct 7, 2024
b3ecc65
add null throws test
kealjones-wk Oct 7, 2024
a956461
format
kealjones-wk Oct 7, 2024
6040cc7
bump react version
kealjones-wk Oct 7, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 50 additions & 17 deletions example/suspense/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,56 @@

<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1">
<meta charset="utf-8">
<title>over_react Suspense example</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1" />
<meta charset="utf-8" />
<title>over_react Suspense example</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />

<!-- javascript -->
<!-- javascript -->
</head>
<body>
<div id="content"></div>

</head>
<body>
<div id="content"></div>

<script src="packages/react/react_prod.js"></script>
<script src="packages/react/react_dom_prod.js"></script>

<script type="application/javascript" defer src="main.dart.js"></script>
</body>
<script src="packages/react/react_prod.js"></script>
<script src="packages/react/react_dom_prod.js"></script>
<script>
const defaultMessageContext = React.createContext('default context value');
window.TestJsComponent = React.forwardRef(function (props, ref) {
const {
buttonProps = {},
listOfProps = [],
inputRef,
buttonComponent = 'button',
inputComponent = 'input',
component = 'span',
children,
messageContext = defaultMessageContext,
...rest
} = props;
let message = React.useContext(messageContext);
if (typeof message !== 'string') {
// Work around react-dart always wrapping values in an object (FED-467)
// whose value is under a property `Symbol('react-dart.context')`.
// Since it's a local symbol, we can't construct a matching symbol, so we find the matching one.
const symbol = Object.getOwnPropertySymbols(message).find((s) =>
s.description.includes('react-dart.context')
);
message = message[symbol];
}
return React.createElement(
'div',
{},
React.createElement(buttonComponent, buttonProps),
React.createElement('li', listOfProps[0]),
React.createElement(inputComponent, { type: 'text', ref: inputRef }),
React.createElement(component, { ...rest, ref }, children),
React.createElement('div', { role: 'alert' }, message)
);
});
</script>
<script type="application/javascript" defer src="main.dart.js"></script>
</body>
</html>
49 changes: 0 additions & 49 deletions example/suspense/lazy.dart

This file was deleted.

64 changes: 56 additions & 8 deletions example/suspense/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,69 @@

import 'dart:html';

import 'package:js/js.dart';
import 'package:over_react/js_component.dart';
import 'package:over_react/over_react.dart';
import 'package:react/react_client/component_factory.dart';
import 'package:react/react_client/react_interop.dart' hide lazy;
import 'package:react/react_dom.dart' as react_dom;
import 'counter_component.dart' deferred as lazy_component;
import 'lazy.dart';
import 'third_party_file.dart';

final LazyCounter = lazy(() async {
await lazy_component.loadLibrary();
part 'main.over_react.g.dart';

@Props(keyNamespace: '')
mixin TestJsProps on UiProps {
@Accessor(key: 'buttonProps')
JsMap? _$raw$buttonProps;

Map? get buttonProps => unjsifyMapProp(_$raw$buttonProps);
set buttonProps(Map? value) => _$raw$buttonProps = jsifyMapProp(value);

@Accessor(key: 'listOfProps')
List<dynamic>? _$raw$listOfProps;

List<Map?>? get listOfProps => unjsifyMapListProp(_$raw$listOfProps);
set listOfProps(List<Map?>? value) => _$raw$listOfProps = jsifyMapListProp(value);

@Accessor(key: 'inputRef')
dynamic _$raw$inputRef;

dynamic get inputRef => unjsifyRefProp(_$raw$inputRef);
set inputRef(dynamic value) => _$raw$inputRef = jsifyRefProp(value);

@Accessor(key: 'messageContext')
ReactContext? _$raw$messageContext;

Context<String?>? get messageContext => unjsifyContextProp(_$raw$messageContext);
set messageContext(Context<String?>? value) => _$raw$messageContext = jsifyContextProp(value);

dynamic /*ElementType*/ component;
dynamic /*ElementType*/ inputComponent;
dynamic /*ElementType*/ buttonComponent;
}

@JS('TestJsComponent')
external ReactClass get _TestJs;

UiFactory<TestJsProps> TestJs = uiJsComponent(
ReactJsComponentFactoryProxy(_TestJs),
_$TestJsConfig, // ignore: undefined_identifier
);

UiFactory<TestJsProps> LazyTestJs = lazy(() async => TestJs,
_$TestJsConfig, // ignore: undefined_identifier
);

UiFactory<CounterPropsMixin> LazyCounter = lazy(() async {
await Future.delayed(Duration(seconds: 5));
await lazy_component.loadLibrary();
return lazy_component.Counter;
},
UiFactoryConfig(
propsFactory: PropsFactory.fromUiFactory(CounterPropsMapView),
displayName: 'This does nothing...',
));
UiFactoryConfig(
propsFactory: PropsFactory.fromUiFactory(CounterPropsMapView)
)
);

void main() {
react_dom.render(
Expand All @@ -38,7 +86,7 @@ void main() {
'I am a fallback UI that will show while we load the lazy component! The load time is artificially inflated to last an additional 5 seconds just to prove it\'s working!',
)
)(
(LazyCounter()..initialCount = 2)(
(LazyTestJs())(
(Dom.div()..id = 'Heyyy!')(),
),
),
Expand Down
1 change: 1 addition & 0 deletions lib/over_react.dart
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ export 'src/util/guid_util.dart';
export 'src/util/hoc.dart';
export 'src/util/handler_chain_util.dart';
export 'src/util/key_constants.dart';
export 'src/util/lazy.dart';
export 'src/util/map_util.dart';
export 'src/util/memo.dart';
export 'src/util/pretty_print.dart';
Expand Down
71 changes: 71 additions & 0 deletions lib/src/util/lazy.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright 2020 Workiva Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

library over_react.lazy;

import 'package:over_react/over_react.dart';
import 'package:react/react.dart' as react;

UiFactory<TProps> lazy<TProps extends UiProps>(
Future<UiFactory<TProps>> Function() loadComponent, /* UiFactoryConfig<TProps> */ dynamic _config,
{bool useJsFactoryProxy = false}) {
ArgumentError.checkNotNull(_config, '_config');

if (_config is! UiFactoryConfig<TProps>) {
throw ArgumentError('_config should be a UiFactoryConfig<TProps>. Make sure you are '
r'using either the generated factory config (i.e. _$FooConfig) or manually '
'declaring your config correctly.');
}
// ignore: invalid_use_of_protected_member
final propsFactory = _config.propsFactory;
ArgumentError.checkNotNull(propsFactory, '_config.propsFactory');
propsFactory!;

final lazyFactoryProxy = react.lazy(() async {
final factory = await loadComponent();
// By using a wrapper uiForwardRef it ensures that we have a matching factory proxy type given to react-dart's lazy,
// a `ReactDartWrappedComponentFactoryProxy`. This is necessary to have consistent prop conversions since we don't
// have access to the original factory proxy outside of this async block.
final wrapper = uiForwardRef<TProps>(
kealjones-wk marked this conversation as resolved.
Show resolved Hide resolved
(props, ref) {
final builder = factory()
..addProps(props)
..ref = ref;
return props.children == null || (props.children != null && props.children?.isEmpty != false)
? builder()
: builder(props.children);
},
UiFactoryConfig(
propsFactory: PropsFactory.fromUiFactory(factory),
kealjones-wk marked this conversation as resolved.
Show resolved Hide resolved
displayName: 'Lazy${_config.displayName}',
),
);
return wrapper().componentFactory!;
});
greglittlefield-wf marked this conversation as resolved.
Show resolved Hide resolved

TProps _uiFactory([Map? props]) {
TProps builder;
if (props == null) {
builder = propsFactory.jsMap(JsBackedMap());
} else if (props is JsBackedMap) {
builder = propsFactory.jsMap(props);
} else {
builder = propsFactory.map(props);
}

return builder..componentFactory = lazyFactoryProxy;
}

return _uiFactory;
}
6 changes: 6 additions & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,9 @@ workiva:
core_checks:
version: 1
react_boilerplate: disabled

dependency_overrides:
react:
git:
url: https://github.com/Workiva/react-dart.git
ref: FED-3114-lazy
Loading
Loading