Skip to content

Commit 583cf18

Browse files
Merge pull request #384 from Workiva/FED-2298_ReactNode_typedef
FED-2298 Add ReactNode typedef
2 parents 61c9899 + be82f05 commit 583cf18

14 files changed

+109
-46
lines changed

Diff for: CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 7.1.0
2+
3+
Add a new `ReactNode` type, which aliases `Object?` to mimic [the React JS Typescript type](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react/v17/index.d.ts#L214).
4+
15
## 7.0.1
26

37
Breaking change - fix nullability/typings for `ReactDom.findDomNode` and `ReactDom.render` from `package:react/react_client/react_interop.dart`:

Diff for: lib/react.dart

+8-17
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,13 @@ export 'package:react/src/react_client/event_helpers.dart';
2424
export 'package:react/react_client/react_interop.dart' show forwardRef2, createRef, memo2;
2525
export 'package:react/src/react_client/synthetic_event_wrappers.dart' hide NonNativeDataTransfer;
2626
export 'package:react/src/react_client/synthetic_data_transfer.dart' show SyntheticDataTransfer;
27-
export 'package:react/src/react_client/event_helpers.dart';
2827

2928
/// A React component declared using a function that takes in [props] and returns rendered output.
3029
///
3130
/// See <https://reactjs.org/docs/components-and-props.html#function-and-class-components>.
3231
///
3332
/// [props] is typed as [JsBackedMap] so that dart2js can optimize props accesses.
34-
typedef DartFunctionComponent = dynamic Function(JsBackedMap props);
33+
typedef DartFunctionComponent = /*ReactNode*/ dynamic Function(JsBackedMap props);
3534

3635
/// The callback to a React forwardRef component. See [forwardRef2] for more details.
3736
///
@@ -40,7 +39,7 @@ typedef DartFunctionComponent = dynamic Function(JsBackedMap props);
4039
/// In the current JS implementation, the ref argument to [React.forwardRef] is usually a JsRef object no matter the input ref type,
4140
/// but according to React the ref argument can be any ref type: https://github.com/facebook/flow/blob/master@%7B2020-09-08%7D/lib/react.js#L305
4241
/// and not just a ref object, so we type [ref] as dynamic here.
43-
typedef DartForwardRefFunctionComponent = dynamic Function(JsBackedMap props, dynamic ref);
42+
typedef DartForwardRefFunctionComponent = /*ReactNode*/ dynamic Function(JsBackedMap props, dynamic ref);
4443

4544
typedef ComponentFactory<T extends Component> = T Function();
4645

@@ -543,12 +542,10 @@ abstract class Component {
543542

544543
/// __Required.__
545544
///
546-
/// When called, it should examine [props] and [state] and return a single child `Element`. This child `Element` can
547-
/// be either a virtual representation of a native DOM component (such as `DivElement`) or another composite
548-
/// `Component` that you've defined yourself.
545+
/// When called, it should examine [props] and [state] and return a [ReactNode].
549546
///
550-
/// See: <https://reactjs.org/docs/react-component.html#render>
551-
dynamic render();
547+
/// See: <https://legacy.reactjs.org/docs/react-component.html#render>
548+
/*ReactNode*/ dynamic render();
552549
}
553550

554551
/// Top-level ReactJS [Component class](https://reactjs.org/docs/react-component.html)
@@ -933,13 +930,7 @@ abstract class Component2 implements Component {
933930
/// See: <https://reactjs.org/docs/typechecking-with-proptypes.html#proptypes>
934931
Map<String, PropValidator<Never>> get propTypes => {};
935932

936-
/// Examines [props] and [state] and returns one of the following types:
937-
///
938-
/// * [ReactElement] (renders a single DOM `Element`)
939-
/// * [Fragment] (renders multiple elements)
940-
/// * [ReactPortal] (renders children into a different DOM subtree)
941-
/// * `String` / `num` (renders text nodes in the DOM)
942-
/// * `bool` / `null` (renders nothing)
933+
/// Examines [props] and [state] and returns a [ReactNode].
943934
///
944935
/// This method is __required__ for class components.
945936
///
@@ -950,9 +941,9 @@ abstract class Component2 implements Component {
950941
/// If you need to interact with the browser / DOM apis, perform your work in [componentDidMount]
951942
/// or the other lifecycle methods instead. Keeping `render` pure makes components easier to think about.
952943
///
953-
/// See: <https://reactjs.org/docs/react-component.html#render>
944+
/// See: <https://legacy.reactjs.org/docs/react-component.html#render>
954945
@override
955-
dynamic render();
946+
/*ReactNode*/ dynamic render();
956947

957948
// ******************************************************************************************************************
958949
//

Diff for: lib/react_client.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export 'package:react/react_client/component_factory.dart'
2020
JsBackedMapComponentFactoryMixin;
2121
export 'package:react/react_client/zone.dart' show componentZone;
2222
export 'package:react/src/react_client/chain_refs.dart' show chainRefs, chainRefList;
23-
export 'package:react/src/typedefs.dart' show JsFunctionComponent;
23+
export 'package:react/src/typedefs.dart' show JsFunctionComponent, ReactNode;
2424

2525
/// Method used to initialize the React environment.
2626
///

Diff for: lib/react_client/component_factory.dart

+8-8
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export 'package:react/src/react_client/factory_util.dart' show generateJsProps;
2222
/// Currently only involves converting a top-level non-[List] [Iterable] to
2323
/// a non-growable [List], but this may be updated in the future to support
2424
/// advanced nesting and other kinds of children.
25-
dynamic listifyChildren(dynamic children) {
25+
ReactNode listifyChildren(ReactNode children) {
2626
if (React.isValidElement(children)) {
2727
// Short-circuit if we're dealing with a ReactElement to avoid the dart2js
2828
// interceptor lookup involved in Dart type-checking.
@@ -63,7 +63,7 @@ Map unconvertJsProps(/* ReactElement|ReactComponent */ instance) {
6363
/// Shared component factory proxy [build] method for components that utilize [JsBackedMap]s.
6464
mixin JsBackedMapComponentFactoryMixin on ReactComponentFactoryProxy {
6565
@override
66-
ReactElement build(Map props, [List childrenArgs = const []]) {
66+
ReactElement build(Map props, [List<ReactNode> childrenArgs = const []]) {
6767
final children = generateChildren(childrenArgs, shouldAlwaysBeList: true);
6868
final convertedProps = generateExtendedJsProps(props);
6969
return React.createElement(type, convertedProps, children);
@@ -89,7 +89,7 @@ class ReactDartComponentFactoryProxy<TComponent extends Component> extends React
8989
ReactClass get type => reactClass;
9090

9191
@override
92-
ReactElement build(Map props, [List childrenArgs = const []]) {
92+
ReactElement build(Map props, [List<ReactNode> childrenArgs = const []]) {
9393
var children = convertArgsToChildren(childrenArgs);
9494
children = listifyChildren(children);
9595

@@ -98,7 +98,7 @@ class ReactDartComponentFactoryProxy<TComponent extends Component> extends React
9898

9999
/// Returns a JavaScript version of the specified [props], preprocessed for consumption by ReactJS and prepared for
100100
/// consumption by the `react` library internals.
101-
static InteropProps generateExtendedJsProps(Map props, dynamic children, {Map? defaultProps}) {
101+
static InteropProps generateExtendedJsProps(Map props, ReactNode children, {Map? defaultProps}) {
102102
if (children == null) {
103103
children = [];
104104
} else if (children is! Iterable) {
@@ -212,8 +212,8 @@ class ReactJsContextComponentFactoryProxy extends ReactJsComponentFactoryProxy {
212212
super(jsClass, shouldConvertDomProps: shouldConvertDomProps);
213213

214214
@override
215-
ReactElement build(Map props, [List childrenArgs = const []]) {
216-
dynamic children = generateChildren(childrenArgs);
215+
ReactElement build(Map props, [List<ReactNode> childrenArgs = const []]) {
216+
var children = generateChildren(childrenArgs);
217217

218218
if (isConsumer) {
219219
if (children is Function) {
@@ -271,7 +271,7 @@ class ReactJsComponentFactoryProxy extends ReactComponentFactoryProxy {
271271
}
272272

273273
@override
274-
ReactElement build(Map props, [List childrenArgs = const []]) {
274+
ReactElement build(Map props, [List<ReactNode> childrenArgs = const []]) {
275275
final children = generateChildren(childrenArgs, shouldAlwaysBeList: alwaysReturnChildrenAsList);
276276
final convertedProps =
277277
generateJsProps(props, convertCallbackRefValue: false, additionalRefPropKeys: _additionalRefPropKeys);
@@ -292,7 +292,7 @@ class ReactDomComponentFactoryProxy extends ReactComponentFactoryProxy {
292292
String get type => name;
293293

294294
@override
295-
ReactElement build(Map props, [List childrenArgs = const []]) {
295+
ReactElement build(Map props, [List<ReactNode> childrenArgs = const []]) {
296296
final children = generateChildren(childrenArgs);
297297
final convertedProps = generateJsProps(props, convertCallbackRefValue: false, wrapWithJsify: true);
298298
return React.createElement(type, convertedProps, children);

Diff for: lib/react_client/react_interop.dart

+6-5
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import 'package:react/react.dart';
1717
import 'package:react/react_client/js_backed_map.dart';
1818
import 'package:react/react_client/component_factory.dart' show ReactDartWrappedComponentFactoryProxy;
1919
import 'package:react/src/react_client/dart2_interop_workaround_bindings.dart';
20+
import 'package:react/src/typedefs.dart';
2021

2122
typedef ReactJsComponentFactory = ReactElement Function(dynamic props, dynamic children);
2223

@@ -27,14 +28,14 @@ typedef ReactJsComponentFactory = ReactElement Function(dynamic props, dynamic c
2728
@JS()
2829
abstract class React {
2930
external static String get version;
30-
external static ReactElement cloneElement(ReactElement element, [JsMap? props, Object? children]);
31+
external static ReactElement cloneElement(ReactElement element, [JsMap? props, ReactNode children]);
3132
external static ReactContext createContext([
3233
dynamic defaultValue,
3334
int Function(dynamic currentValue, dynamic nextValue)? calculateChangedBits,
3435
]);
3536
@Deprecated('For internal use only.')
3637
external static ReactClass createClass(ReactClassConfig reactClassConfig);
37-
external static ReactElement createElement(dynamic type, props, [Object? children]);
38+
external static ReactElement createElement(dynamic type, props, [ReactNode children]);
3839
external static JsRef createRef();
3940
external static ReactClass forwardRef(Function(JsMap props, dynamic ref) wrapperFunction);
4041
external static ReactClass memo(
@@ -274,8 +275,8 @@ ReactComponentFactoryProxy memo2(ReactComponentFactoryProxy factory,
274275
}
275276

276277
abstract class ReactDom {
277-
static Element? findDOMNode(dynamic object) => ReactDOM.findDOMNode(object);
278-
static dynamic render(dynamic component, Element element) => ReactDOM.render(component, element);
278+
static Element? findDOMNode(ReactNode object) => ReactDOM.findDOMNode(object);
279+
static dynamic render(ReactNode component, Element element) => ReactDOM.render(component, element);
279280
static bool unmountComponentAtNode(Element element) => ReactDOM.unmountComponentAtNode(element);
280281

281282
/// Returns a a portal that renders [children] into a [container].
@@ -285,7 +286,7 @@ abstract class ReactDom {
285286
/// [children] can be any renderable React child, such as a [ReactElement], [String], or fragment.
286287
///
287288
/// See: <https://reactjs.org/docs/portals.html>
288-
static ReactPortal createPortal(dynamic children, Element container) => ReactDOM.createPortal(children, container);
289+
static ReactPortal createPortal(ReactNode children, Element container) => ReactDOM.createPortal(children, container);
289290
}
290291

291292
@JS('ReactDOMServer')

Diff for: lib/src/react_client/dart2_interop_workaround_bindings.dart

+4-3
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ import 'dart:html';
55

66
import 'package:js/js.dart';
77
import 'package:react/react_client/react_interop.dart';
8+
import 'package:react/src/typedefs.dart';
89

910
@JS()
1011
abstract class ReactDOM {
11-
external static Element? findDOMNode(dynamic object);
12-
external static dynamic render(dynamic component, Element element);
12+
external static Element? findDOMNode(ReactNode object);
13+
external static dynamic render(ReactNode component, Element element);
1314
external static bool unmountComponentAtNode(Element element);
14-
external static ReactPortal createPortal(dynamic children, Element container);
15+
external static ReactPortal createPortal(ReactNode children, Element container);
1516
}

Diff for: lib/src/react_client/dart_interop_statics.dart

+2-2
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ final ReactDartInteropStatics dartInteropStatics = (() {
180180
});
181181

182182
/// Wrapper for [Component.render].
183-
dynamic handleRender(Component component) => zone.run(() {
183+
ReactNode handleRender(Component component) => zone.run(() {
184184
return component.render();
185185
});
186186

@@ -317,7 +317,7 @@ abstract class ReactDartInteropStatics2 {
317317
}
318318
});
319319

320-
static dynamic handleRender(Component2 component, JsMap jsProps, JsMap jsState, dynamic jsContext) => // dartfmt
320+
static ReactNode handleRender(Component2 component, JsMap jsProps, JsMap jsState, dynamic jsContext) => // dartfmt
321321
// ignore: invalid_use_of_visible_for_testing_member
322322
componentZone.run(() {
323323
_updatePropsAndStateWithJs(component, jsProps, jsState);

Diff for: lib/src/react_client/factory_util.dart

+3-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import 'package:react/react_client/js_backed_map.dart';
88
import 'package:react/react_client/js_interop_helpers.dart';
99
import 'package:react/react_client/react_interop.dart';
1010
import 'package:react/src/react_client/internal_react_interop.dart';
11+
import 'package:react/src/typedefs.dart';
1112

1213
/// Converts a list of variadic children arguments to children that should be passed to ReactJS.
1314
///
@@ -16,7 +17,7 @@ import 'package:react/src/react_client/internal_react_interop.dart';
1617
/// - `null` if there are no args
1718
/// - the single child if only one was specified
1819
/// - otherwise, the same list of args, will all top-level children validated
19-
dynamic convertArgsToChildren(List childrenArgs) {
20+
ReactNode convertArgsToChildren(List childrenArgs) {
2021
if (childrenArgs.isEmpty) {
2122
return null;
2223
} else if (childrenArgs.length == 1) {
@@ -98,7 +99,7 @@ bool isRefArgumentDefinitelyNonNullable(Function(Never) callbackRef) {
9899
/// - `[]` if there are no args and [shouldAlwaysBeList] is true
99100
/// - the single child if only one was specified
100101
/// - otherwise, the same list of args, will all top-level children validated
101-
dynamic generateChildren(List childrenArgs, {bool shouldAlwaysBeList = false}) {
102+
ReactNode generateChildren(List childrenArgs, {bool shouldAlwaysBeList = false}) {
102103
var children;
103104

104105
if (childrenArgs.isEmpty) {

Diff for: lib/src/react_client/internal_react_interop.dart

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import 'package:react/react_client/bridge.dart';
1111
import 'package:react/react_client/js_backed_map.dart';
1212
import 'package:react/react_client/react_interop.dart'
1313
show React, ReactClass, ReactComponent, ReactDartComponentInternal;
14+
import 'package:react/src/typedefs.dart';
1415

1516
/// A JavaScript interop class representing a value in a React JS `context` object.
1617
///
@@ -116,7 +117,7 @@ class ReactDartInteropStatics {
116117
void Function(Component component, InteropContextValue nextContext) handleComponentWillUpdate,
117118
void Function(Component component, ReactDartComponentInternal prevInternal) handleComponentDidUpdate,
118119
void Function(Component component) handleComponentWillUnmount,
119-
dynamic Function(Component component) handleRender,
120+
ReactNode Function(Component component) handleRender,
120121
});
121122
}
122123

Diff for: lib/src/typedefs.dart

+14-2
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ typedef CallbackRef<T> = Function(T? componentOrDomNode);
1313
/// - [props] will always be supplied as the first argument
1414
/// - [legacyContext] has been deprecated and should not be used but remains for backward compatibility and is necessary
1515
/// to match Dart's generated call signature based on the number of args React provides.
16-
typedef JsFunctionComponent = dynamic Function(JsMap props, [JsMap? legacyContext]);
16+
typedef JsFunctionComponent = /*ReactNode*/ dynamic Function(JsMap props, [JsMap? legacyContext]);
1717

18-
typedef JsForwardRefFunctionComponent = dynamic Function(JsMap props, dynamic ref);
18+
typedef JsForwardRefFunctionComponent = /*ReactNode*/ dynamic Function(JsMap props, dynamic ref);
1919

2020
/// Typedef for `react.Component.ref`, which should return one of the following specified by the provided [ref]:
2121
///
@@ -32,3 +32,15 @@ typedef StateUpdaterCallback = Map? Function(Map prevState, Map props);
3232
///
3333
/// See: <https://reactjs.org/docs/react-component.html#setstate>
3434
typedef SetStateCallback = Function();
35+
36+
/// A value that can be returned from a component's `render`, or used as `children`.
37+
///
38+
/// Possible values include:
39+
/// * A `ReactElement` such as a DOM element created using `react.div({})`, or a user-defined component.
40+
/// * A `ReactPortal` created by `createPortal`.
41+
/// * A `String` or `num` (Rendered as text nodes in the DOM).
42+
/// * Booleans or `null` (Render nothing).
43+
/// * A list of, or a `ReactFragment` containing, any/all of the above.
44+
///
45+
/// See also: https://github.com/facebook/react/blob/b3003047101b4c7a643788a8faf576f7e370fb45/packages/shared/ReactTypes.js#L10
46+
typedef ReactNode = Object?;

Diff for: pubspec.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ version: 7.0.1
33
description: Bindings of the ReactJS library for building interactive interfaces.
44
homepage: https://github.com/cleandart/react-dart
55
environment:
6-
sdk: '>=2.12.0 <3.0.0'
6+
sdk: '>=2.13.0 <3.0.0'
77
dependencies:
88
js: ^0.6.3
99
meta: ^1.6.0

Diff for: test/forward_ref_test.dart

+3-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@ main() {
3636

3737
test('when displayName argument is passed to forwardRef2', () {
3838
const name = 'ForwardRefTestComponent';
39-
final ForwardRefTestComponent = forwardRef2((props, ref) {}, displayName: name);
39+
final ForwardRefTestComponent = forwardRef2((props, ref) {
40+
return null;
41+
}, displayName: name);
4042
expect(getProperty(getProperty(ForwardRefTestComponent.type as Object, 'render'), 'name'), name);
4143
});
4244
});

0 commit comments

Comments
 (0)