Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 7.1.0

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).

## 7.0.1

Breaking change - fix nullability/typings for `ReactDom.findDomNode` and `ReactDom.render` from `package:react/react_client/react_interop.dart`:
Expand Down
25 changes: 8 additions & 17 deletions lib/react.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,13 @@ export 'package:react/src/react_client/event_helpers.dart';
export 'package:react/react_client/react_interop.dart' show forwardRef2, createRef, memo2;
export 'package:react/src/react_client/synthetic_event_wrappers.dart' hide NonNativeDataTransfer;
export 'package:react/src/react_client/synthetic_data_transfer.dart' show SyntheticDataTransfer;
export 'package:react/src/react_client/event_helpers.dart';

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

/// The callback to a React forwardRef component. See [forwardRef2] for more details.
///
Expand All @@ -40,7 +39,7 @@ typedef DartFunctionComponent = dynamic Function(JsBackedMap props);
/// In the current JS implementation, the ref argument to [React.forwardRef] is usually a JsRef object no matter the input ref type,
/// 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
/// and not just a ref object, so we type [ref] as dynamic here.
typedef DartForwardRefFunctionComponent = dynamic Function(JsBackedMap props, dynamic ref);
typedef DartForwardRefFunctionComponent = /*ReactNode*/ dynamic Function(JsBackedMap props, dynamic ref);

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

Expand Down Expand Up @@ -543,12 +542,10 @@ abstract class Component {

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

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

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

// ******************************************************************************************************************
//
Expand Down
2 changes: 1 addition & 1 deletion lib/react_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export 'package:react/react_client/component_factory.dart'
JsBackedMapComponentFactoryMixin;
export 'package:react/react_client/zone.dart' show componentZone;
export 'package:react/src/react_client/chain_refs.dart' show chainRefs, chainRefList;
export 'package:react/src/typedefs.dart' show JsFunctionComponent;
export 'package:react/src/typedefs.dart' show JsFunctionComponent, ReactNode;

/// Method used to initialize the React environment.
///
Expand Down
16 changes: 8 additions & 8 deletions lib/react_client/component_factory.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export 'package:react/src/react_client/factory_util.dart' show generateJsProps;
/// Currently only involves converting a top-level non-[List] [Iterable] to
/// a non-growable [List], but this may be updated in the future to support
/// advanced nesting and other kinds of children.
dynamic listifyChildren(dynamic children) {
ReactNode listifyChildren(ReactNode children) {
if (React.isValidElement(children)) {
// Short-circuit if we're dealing with a ReactElement to avoid the dart2js
// interceptor lookup involved in Dart type-checking.
Expand Down Expand Up @@ -63,7 +63,7 @@ Map unconvertJsProps(/* ReactElement|ReactComponent */ instance) {
/// Shared component factory proxy [build] method for components that utilize [JsBackedMap]s.
mixin JsBackedMapComponentFactoryMixin on ReactComponentFactoryProxy {
@override
ReactElement build(Map props, [List childrenArgs = const []]) {
ReactElement build(Map props, [List<ReactNode> childrenArgs = const []]) {
final children = generateChildren(childrenArgs, shouldAlwaysBeList: true);
final convertedProps = generateExtendedJsProps(props);
return React.createElement(type, convertedProps, children);
Expand All @@ -89,7 +89,7 @@ class ReactDartComponentFactoryProxy<TComponent extends Component> extends React
ReactClass get type => reactClass;

@override
ReactElement build(Map props, [List childrenArgs = const []]) {
ReactElement build(Map props, [List<ReactNode> childrenArgs = const []]) {
var children = convertArgsToChildren(childrenArgs);
children = listifyChildren(children);

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

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

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

if (isConsumer) {
if (children is Function) {
Expand Down Expand Up @@ -271,7 +271,7 @@ class ReactJsComponentFactoryProxy extends ReactComponentFactoryProxy {
}

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

@override
ReactElement build(Map props, [List childrenArgs = const []]) {
ReactElement build(Map props, [List<ReactNode> childrenArgs = const []]) {
final children = generateChildren(childrenArgs);
final convertedProps = generateJsProps(props, convertCallbackRefValue: false, wrapWithJsify: true);
return React.createElement(type, convertedProps, children);
Expand Down
11 changes: 6 additions & 5 deletions lib/react_client/react_interop.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import 'package:react/react.dart';
import 'package:react/react_client/js_backed_map.dart';
import 'package:react/react_client/component_factory.dart' show ReactDartWrappedComponentFactoryProxy;
import 'package:react/src/react_client/dart2_interop_workaround_bindings.dart';
import 'package:react/src/typedefs.dart';

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

Expand All @@ -27,14 +28,14 @@ typedef ReactJsComponentFactory = ReactElement Function(dynamic props, dynamic c
@JS()
abstract class React {
external static String get version;
external static ReactElement cloneElement(ReactElement element, [JsMap? props, Object? children]);
external static ReactElement cloneElement(ReactElement element, [JsMap? props, ReactNode children]);
external static ReactContext createContext([
dynamic defaultValue,
int Function(dynamic currentValue, dynamic nextValue)? calculateChangedBits,
]);
@Deprecated('For internal use only.')
external static ReactClass createClass(ReactClassConfig reactClassConfig);
external static ReactElement createElement(dynamic type, props, [Object? children]);
external static ReactElement createElement(dynamic type, props, [ReactNode children]);
external static JsRef createRef();
external static ReactClass forwardRef(Function(JsMap props, dynamic ref) wrapperFunction);
external static ReactClass memo(
Expand Down Expand Up @@ -274,8 +275,8 @@ ReactComponentFactoryProxy memo2(ReactComponentFactoryProxy factory,
}

abstract class ReactDom {
static Element? findDOMNode(dynamic object) => ReactDOM.findDOMNode(object);
static dynamic render(dynamic component, Element element) => ReactDOM.render(component, element);
static Element? findDOMNode(ReactNode object) => ReactDOM.findDOMNode(object);
static dynamic render(ReactNode component, Element element) => ReactDOM.render(component, element);
static bool unmountComponentAtNode(Element element) => ReactDOM.unmountComponentAtNode(element);

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

@JS('ReactDOMServer')
Expand Down
7 changes: 4 additions & 3 deletions lib/src/react_client/dart2_interop_workaround_bindings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import 'dart:html';

import 'package:js/js.dart';
import 'package:react/react_client/react_interop.dart';
import 'package:react/src/typedefs.dart';

@JS()
abstract class ReactDOM {
external static Element? findDOMNode(dynamic object);
external static dynamic render(dynamic component, Element element);
external static Element? findDOMNode(ReactNode object);
external static dynamic render(ReactNode component, Element element);
external static bool unmountComponentAtNode(Element element);
external static ReactPortal createPortal(dynamic children, Element container);
external static ReactPortal createPortal(ReactNode children, Element container);
}
4 changes: 2 additions & 2 deletions lib/src/react_client/dart_interop_statics.dart
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ final ReactDartInteropStatics dartInteropStatics = (() {
});

/// Wrapper for [Component.render].
dynamic handleRender(Component component) => zone.run(() {
ReactNode handleRender(Component component) => zone.run(() {
return component.render();
});

Expand Down Expand Up @@ -317,7 +317,7 @@ abstract class ReactDartInteropStatics2 {
}
});

static dynamic handleRender(Component2 component, JsMap jsProps, JsMap jsState, dynamic jsContext) => // dartfmt
static ReactNode handleRender(Component2 component, JsMap jsProps, JsMap jsState, dynamic jsContext) => // dartfmt
// ignore: invalid_use_of_visible_for_testing_member
componentZone.run(() {
_updatePropsAndStateWithJs(component, jsProps, jsState);
Expand Down
5 changes: 3 additions & 2 deletions lib/src/react_client/factory_util.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'package:react/react_client/js_backed_map.dart';
import 'package:react/react_client/js_interop_helpers.dart';
import 'package:react/react_client/react_interop.dart';
import 'package:react/src/react_client/internal_react_interop.dart';
import 'package:react/src/typedefs.dart';

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

if (childrenArgs.isEmpty) {
Expand Down
3 changes: 2 additions & 1 deletion lib/src/react_client/internal_react_interop.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import 'package:react/react_client/bridge.dart';
import 'package:react/react_client/js_backed_map.dart';
import 'package:react/react_client/react_interop.dart'
show React, ReactClass, ReactComponent, ReactDartComponentInternal;
import 'package:react/src/typedefs.dart';

/// A JavaScript interop class representing a value in a React JS `context` object.
///
Expand Down Expand Up @@ -116,7 +117,7 @@ class ReactDartInteropStatics {
void Function(Component component, InteropContextValue nextContext) handleComponentWillUpdate,
void Function(Component component, ReactDartComponentInternal prevInternal) handleComponentDidUpdate,
void Function(Component component) handleComponentWillUnmount,
dynamic Function(Component component) handleRender,
ReactNode Function(Component component) handleRender,
});
}

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

typedef JsForwardRefFunctionComponent = dynamic Function(JsMap props, dynamic ref);
typedef JsForwardRefFunctionComponent = /*ReactNode*/ dynamic Function(JsMap props, dynamic ref);

/// Typedef for `react.Component.ref`, which should return one of the following specified by the provided [ref]:
///
Expand All @@ -32,3 +32,15 @@ typedef StateUpdaterCallback = Map? Function(Map prevState, Map props);
///
/// See: <https://reactjs.org/docs/react-component.html#setstate>
typedef SetStateCallback = Function();

/// A value that can be returned from a component's `render`, or used as `children`.
///
/// Possible values include:
/// * A `ReactElement` such as a DOM element created using `react.div({})`, or a user-defined component.
/// * A `ReactPortal` created by `createPortal`.
/// * A `String` or `num` (Rendered as text nodes in the DOM).
/// * Booleans or `null` (Render nothing).
/// * A list of, or a `ReactFragment` containing, any/all of the above.
///
/// See also: https://github.com/facebook/react/blob/b3003047101b4c7a643788a8faf576f7e370fb45/packages/shared/ReactTypes.js#L10
typedef ReactNode = Object?;
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ version: 7.0.1
description: Bindings of the ReactJS library for building interactive interfaces.
homepage: https://github.com/cleandart/react-dart
environment:
sdk: '>=2.12.0 <3.0.0'
sdk: '>=2.13.0 <3.0.0'
dependencies:
js: ^0.6.3
meta: ^1.6.0
Expand Down
4 changes: 3 additions & 1 deletion test/forward_ref_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ main() {

test('when displayName argument is passed to forwardRef2', () {
const name = 'ForwardRefTestComponent';
final ForwardRefTestComponent = forwardRef2((props, ref) {}, displayName: name);
final ForwardRefTestComponent = forwardRef2((props, ref) {
return null;
}, displayName: name);
expect(getProperty(getProperty(ForwardRefTestComponent.type as Object, 'render'), 'name'), name);
});
});
Expand Down
Loading