-
Notifications
You must be signed in to change notification settings - Fork 67
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #406 from Workiva/FED-3114-lazy
FED-3207 Add react lazy
- Loading branch information
Showing
8 changed files
with
268 additions
and
72 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,55 +1,39 @@ | ||
@JS() | ||
library js_components; | ||
library example.suspense.suspense; | ||
|
||
import 'dart:html'; | ||
import 'dart:js_util'; | ||
|
||
import 'package:js/js.dart'; | ||
import 'package:react/hooks.dart'; | ||
import 'package:react/react.dart' as react; | ||
import 'package:react/react_client.dart'; | ||
import 'package:react/react_client/react_interop.dart'; | ||
import 'package:react/react_dom.dart' as react_dom; | ||
import 'package:react/src/js_interop_util.dart'; | ||
import './simple_component.dart' deferred as simple; | ||
|
||
@JS('React.lazy') | ||
external ReactClass jsLazy(Promise Function() factory); | ||
|
||
// Only intended for testing purposes, Please do not copy/paste this into repo. | ||
// This will most likely be added to the PUBLIC api in the future, | ||
// but needs more testing and Typing decisions to be made first. | ||
ReactJsComponentFactoryProxy lazy(Future<ReactComponentFactoryProxy> Function() factory) => | ||
ReactJsComponentFactoryProxy( | ||
jsLazy( | ||
allowInterop( | ||
() => futureToPromise( | ||
// React.lazy only supports "default exports" from a module. | ||
// This `{default: yourExport}` workaround can be found in the React.lazy RFC comments. | ||
// See: https://github.com/reactjs/rfcs/pull/64#issuecomment-431507924 | ||
(() async => jsify({'default': (await factory()).type}))(), | ||
), | ||
), | ||
), | ||
); | ||
|
||
main() { | ||
final content = wrapper({}); | ||
|
||
react_dom.render(content, querySelector('#content')); | ||
} | ||
|
||
final lazyComponent = lazy(() async { | ||
await simple.loadLibrary(); | ||
final lazyComponent = react.lazy(() async { | ||
await Future.delayed(Duration(seconds: 5)); | ||
await simple.loadLibrary(); | ||
|
||
return simple.SimpleComponent; | ||
}); | ||
|
||
var wrapper = react.registerFunctionComponent(WrapperComponent, displayName: 'wrapper'); | ||
|
||
WrapperComponent(Map props) { | ||
final showComponent = useState(false); | ||
return react.div({ | ||
'id': 'lazy-wrapper' | ||
}, [ | ||
react.Suspense({'fallback': 'Loading...'}, [lazyComponent({})]) | ||
react.button({ | ||
'onClick': (_) { | ||
showComponent.set(!showComponent.value); | ||
} | ||
}, 'Toggle component'), | ||
react.Suspense({'fallback': 'Loading...'}, showComponent.value ? lazyComponent({}) : null) | ||
]); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import 'dart:js'; | ||
import 'dart:js_util'; | ||
|
||
import 'package:react/react.dart'; | ||
import 'package:react/react_client/component_factory.dart'; | ||
import 'package:react/react_client/react_interop.dart'; | ||
import 'package:react/src/js_interop_util.dart'; | ||
|
||
/// Defer loading a component's code until it is rendered for the first time. | ||
/// | ||
/// The `lazy` function is used to create lazy components in react-dart. Lazy components are able to run asynchronous code only when they are trying to be rendered for the first time, allowing for deferred loading of the component's code. | ||
/// | ||
/// To use the `lazy` function, you need to wrap the lazy component with a `Suspense` component. The `Suspense` component allows you to specify what should be displayed while the lazy component is loading, such as a loading spinner or a placeholder. | ||
/// | ||
/// Example usage: | ||
/// ```dart | ||
/// import 'package:react/react.dart' show lazy, Suspense; | ||
/// import './simple_component.dart' deferred as simple; | ||
/// | ||
/// final lazyComponent = lazy(() async { | ||
/// await simple.loadLibrary(); | ||
/// return simple.SimpleComponent; | ||
/// }); | ||
/// | ||
/// // Wrap the lazy component with Suspense | ||
/// final app = Suspense( | ||
/// { | ||
/// fallback: 'Loading...', | ||
/// }, | ||
/// lazyComponent({}), | ||
/// ); | ||
/// ``` | ||
/// | ||
/// Defer loading a component’s code until it is rendered for the first time. | ||
/// | ||
/// Lazy components need to be wrapped with `Suspense` to render. | ||
/// `Suspense` also allows you to specify what should be displayed while the lazy component is loading. | ||
ReactComponentFactoryProxy lazy(Future<ReactComponentFactoryProxy> Function() load) { | ||
final hoc = React.lazy( | ||
allowInterop( | ||
() => futureToPromise( | ||
Future.sync(() async { | ||
final factory = await load(); | ||
// 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 = forwardRef2((props, ref) { | ||
final children = props['children']; | ||
return factory.build( | ||
{...props, 'ref': ref}, | ||
[ | ||
if (children != null && !(children is List && children.isEmpty)) children, | ||
], | ||
); | ||
}, displayName: 'LazyWrapper(${_getComponentName(factory.type) ?? 'Anonymous'})'); | ||
return jsify({'default': wrapper.type}); | ||
}), | ||
), | ||
), | ||
); | ||
|
||
// Setting this version and wrapping with ReactDartWrappedComponentFactoryProxy | ||
// is only okay because it matches the version and factory proxy of the wrapperFactory above. | ||
// ignore: invalid_use_of_protected_member | ||
setProperty(hoc, 'dartComponentVersion', ReactDartComponentVersion.component2); | ||
return ReactDartWrappedComponentFactoryProxy(hoc); | ||
} | ||
|
||
String? _getComponentName(Object? type) { | ||
if (type == null) return null; | ||
|
||
if (type is String) return type; | ||
|
||
final name = getProperty(type, 'name'); | ||
if (name is String) return name; | ||
|
||
final displayName = getProperty(type, 'displayName'); | ||
if (displayName is String) return displayName; | ||
|
||
return null; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.