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

CPLAT-10894 Rethrow and print registerComponent/registerComponent2 errors #261

Merged
merged 2 commits into from Jul 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
122 changes: 73 additions & 49 deletions lib/react_client.dart
Expand Up @@ -629,32 +629,37 @@ ReactDartComponentFactoryProxy _registerComponent(
ComponentFactory componentFactory, [
Iterable<String> skipMethods = const ['getDerivedStateFromError', 'componentDidCatch'],
]) {
var componentInstance = componentFactory();

if (componentInstance is Component2) {
return _registerComponent2(componentFactory, skipMethods: skipMethods);
}

var componentStatics = new ComponentStatics(componentFactory);
try {
var componentInstance = componentFactory();

var jsConfig = new JsComponentConfig(
childContextKeys: componentInstance.childContextKeys,
contextKeys: componentInstance.contextKeys,
);
if (componentInstance is Component2) {
return _registerComponent2(componentFactory, skipMethods: skipMethods);
}

/// Create the JS [`ReactClass` component class](https://facebook.github.io/react/docs/top-level-api.html#react.createclass)
/// with custom JS lifecycle methods.
var reactComponentClass = createReactDartComponentClass(_dartInteropStatics, componentStatics, jsConfig)
// ignore: invalid_use_of_protected_member
..dartComponentVersion = ReactDartComponentVersion.component
..displayName = componentFactory().displayName;
var componentStatics = new ComponentStatics(componentFactory);

// Cache default props and store them on the ReactClass so they can be used
// by ReactDartComponentFactoryProxy and externally.
final Map defaultProps = new Map.unmodifiable(componentInstance.getDefaultProps());
reactComponentClass.dartDefaultProps = defaultProps;
var jsConfig = new JsComponentConfig(
childContextKeys: componentInstance.childContextKeys,
contextKeys: componentInstance.contextKeys,
);

return new ReactDartComponentFactoryProxy(reactComponentClass);
/// Create the JS [`ReactClass` component class](https://facebook.github.io/react/docs/top-level-api.html#react.createclass)
/// with custom JS lifecycle methods.
var reactComponentClass = createReactDartComponentClass(_dartInteropStatics, componentStatics, jsConfig)
// ignore: invalid_use_of_protected_member
..dartComponentVersion = ReactDartComponentVersion.component
..displayName = componentFactory().displayName;

// Cache default props and store them on the ReactClass so they can be used
// by ReactDartComponentFactoryProxy and externally.
final Map defaultProps = new Map.unmodifiable(componentInstance.getDefaultProps());
reactComponentClass.dartDefaultProps = defaultProps;

return new ReactDartComponentFactoryProxy(reactComponentClass);
} catch (e, stack) {
print('Error when registering Component: $e\n$stack');
rethrow;
}
}

/// Creates ReactJS [ReactElement] instances for `JSContext` components.
Expand Down Expand Up @@ -775,39 +780,58 @@ ReactDartComponentFactoryProxy2 _registerComponent2(
Iterable<String> skipMethods = const ['getDerivedStateFromError', 'componentDidCatch'],
Component2BridgeFactory bridgeFactory,
}) {
bridgeFactory ??= Component2BridgeImpl.bridgeFactory;
bool errorPrinted = false;
try {
bridgeFactory ??= Component2BridgeImpl.bridgeFactory;

final componentInstance = componentFactory();
final componentStatics = new ComponentStatics2(
componentFactory: componentFactory,
instanceForStaticMethods: componentInstance,
bridgeFactory: bridgeFactory,
);
final filteredSkipMethods = _filterSkipMethods(skipMethods);
final componentInstance = componentFactory();
final componentStatics = new ComponentStatics2(
componentFactory: componentFactory,
instanceForStaticMethods: componentInstance,
bridgeFactory: bridgeFactory,
);
final filteredSkipMethods = _filterSkipMethods(skipMethods);

// Cache default props and store them on the ReactClass so they can be used
// by ReactDartComponentFactoryProxy and externally.
final JsBackedMap defaultProps = new JsBackedMap.from(componentInstance.defaultProps);
// Cache default props and store them on the ReactClass so they can be used
// by ReactDartComponentFactoryProxy and externally.
JsBackedMap defaultProps;
try {
defaultProps = JsBackedMap.from(componentInstance.defaultProps);
} catch (e, stack) {
print('Error when registering Component2 when getting defaultProps: $e\n$stack');
errorPrinted = true;
rethrow;
}

final JsMap jsPropTypes =
bridgeFactory(componentInstance).jsifyPropTypes(componentInstance, componentInstance.propTypes);
JsMap jsPropTypes;
try {
jsPropTypes = bridgeFactory(componentInstance).jsifyPropTypes(componentInstance, componentInstance.propTypes);
} catch (e, stack) {
print('Error when registering Component2 when getting propTypes: $e\n$stack');
errorPrinted = true;
rethrow;
}

var jsConfig2 = new JsComponentConfig2(
defaultProps: defaultProps.jsObject,
contextType: componentInstance.contextType?.jsThis,
skipMethods: filteredSkipMethods,
propTypes: jsPropTypes,
);
var jsConfig2 = new JsComponentConfig2(
defaultProps: defaultProps.jsObject,
contextType: componentInstance.contextType?.jsThis,
skipMethods: filteredSkipMethods,
propTypes: jsPropTypes,
);

/// Create the JS [`ReactClass` component class](https://facebook.github.io/react/docs/top-level-api.html#react.createclass)
/// with custom JS lifecycle methods.
var reactComponentClass =
createReactDartComponentClass2(_ReactDartInteropStatics2.staticsForJs, componentStatics, jsConfig2)
..displayName = componentInstance.displayName;
// ignore: invalid_use_of_protected_member
reactComponentClass.dartComponentVersion = ReactDartComponentVersion.component2;
/// Create the JS [`ReactClass` component class](https://facebook.github.io/react/docs/top-level-api.html#react.createclass)
/// with custom JS lifecycle methods.
var reactComponentClass =
createReactDartComponentClass2(_ReactDartInteropStatics2.staticsForJs, componentStatics, jsConfig2)
..displayName = componentInstance.displayName;
// ignore: invalid_use_of_protected_member
reactComponentClass.dartComponentVersion = ReactDartComponentVersion.component2;

return new ReactDartComponentFactoryProxy2(reactComponentClass);
return new ReactDartComponentFactoryProxy2(reactComponentClass);
} catch (e, stack) {
if (!errorPrinted) print('Error when registering Component2: $e\n$stack');
rethrow;
}
}

/// Creates ReactJS [ReactElement] instances for DOM components.
Expand Down
68 changes: 68 additions & 0 deletions test/react_client_test.dart
Expand Up @@ -173,6 +173,46 @@ main() {
expect(result, isNull);
});
});

group('registerComponent', () {
test('throws with printed error', () {
expect(() => react.registerComponent(() => ThrowsInDefaultPropsComponent()), throwsStateError);
expect(() {
try {
react.registerComponent(() => ThrowsInDefaultPropsComponent());
} catch (_) {}
}, prints(contains('Error when registering Component:')));
});
});

group('registerComponent2', () {
test('throws with specific error when defaultProps throws', () {
expect(() => react.registerComponent2(() => ThrowsInDefaultPropsComponent2()), throwsStateError);
expect(() {
try {
react.registerComponent2(() => ThrowsInDefaultPropsComponent2());
} catch (_) {}
}, prints(contains('Error when registering Component2 when getting defaultProps')));
});

test('throws with specific error when propTypes throws', () {
expect(() => react.registerComponent2(() => ThrowsInPropTypesComponent2()), throwsStateError);
expect(() {
try {
react.registerComponent2(() => ThrowsInPropTypesComponent2());
} catch (_) {}
}, prints(contains('Error when registering Component2 when getting propTypes')));
});

test('throws with generic error when something else throws', () {
expect(() => react.registerComponent2(() => throw StateError('bad component')), throwsStateError);
expect(() {
try {
react.registerComponent2(() => throw StateError('bad component'));
} catch (_) {}
}, prints(contains('Error when registering Component2:')));
});
});
}

@JS()
Expand All @@ -187,6 +227,34 @@ final Function testJsComponentFactory = (() {
};
})();

class ThrowsInDefaultPropsComponent extends Component {
@override
Map getDefaultProps() => throw StateError('bad default props');

@override
render() {
return null;
}
}

class ThrowsInDefaultPropsComponent2 extends Component2 {
get defaultProps => throw StateError('bad default props');

@override
render() {
return null;
}
}

class ThrowsInPropTypesComponent2 extends Component2 {
get propTypes => throw StateError('bad prop types');

@override
render() {
return null;
}
}

class DartComponent2Component extends Component2 {
@override
render() {
Expand Down