Skip to content

Commit f808ac8

Browse files
authored
Merge pull request #221 from cleandart/CPLAT-3874-function-components
CPLAT-3874: Function Components
2 parents 8bb11d0 + a6278e0 commit f808ac8

27 files changed

+668
-245
lines changed

.travis.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,6 @@ before_script:
1717
- pub run dependency_validator -i build_runner,build_test,build_web_compilers
1818

1919
script:
20-
- pub run build_runner test --release -- -p chrome
21-
- pub run build_runner test -- -p chrome
20+
# TODO once production is above 2.4.1 re-check the `fails-on-2.4.1` tagged tests
21+
- pub run build_runner test --release -- -p chrome -x fails-on-241
22+
- pub run build_runner test -- -p chrome -x fails-on-241

dart_test.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
tags:
2+
"fails-on-241":

example/index.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ <h2 class="display-5">Use Case / Test Examples</h2>
5252
<li>
5353
<a href="js_components/index.html"><code>JsComponents</code> Example</a>
5454
</li>
55+
<li>
56+
<a href="test/function_component_test.html"><code>Function Component</code> test</a>
57+
</li>
5558
</ul>
5659

5760
</div>
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import 'dart:html';
2+
3+
import 'package:react/react.dart' as react;
4+
import 'package:react/react_dom.dart' as react_dom;
5+
import 'package:react/react_client.dart';
6+
7+
import 'react_test_components.dart';
8+
9+
void main() {
10+
setClientConfiguration();
11+
var inputValue = 'World';
12+
// TODO: replace this with hooks/useState when they are added.
13+
render() {
14+
react_dom.render(
15+
react.Fragment({}, [
16+
react.input(
17+
{
18+
'defaultValue': inputValue,
19+
'onChange': (event) {
20+
inputValue = event.currentTarget.value;
21+
render();
22+
}
23+
},
24+
),
25+
react.br({}),
26+
helloGregFunctionComponent({'key': 'greg'}),
27+
react.br({}),
28+
helloGregFunctionComponent({'key': 'not greg'}, inputValue)
29+
]),
30+
querySelector('#content'));
31+
}
32+
33+
render();
34+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
6+
7+
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
8+
9+
<title>React Dart Examples: function component test</title>
10+
</head>
11+
<body>
12+
<div id="content" class="container"></div>
13+
<script src="packages/react/react.js"></script>
14+
<script src="packages/react/react_dom.js"></script>
15+
<script src="function_component_test.dart.js"></script>
16+
</body>
17+
</html>

example/test/react_test_components.dart

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -283,9 +283,9 @@ class _NewContextProviderComponent extends react.Component2 {
283283
}
284284

285285
render() {
286-
Map provideMap = {'renderCount': this.state['renderCount']};
286+
final provideMap = {'renderCount': this.state['renderCount']};
287287

288-
Map complexValues = {
288+
final complexValues = {
289289
'callback': printMe,
290290
'dartComponent': newContextRefComponent,
291291
'map': {
@@ -393,6 +393,16 @@ class _NewContextTypeConsumerComponent extends react.Component2 {
393393
}
394394
}
395395

396+
var helloGregFunctionComponent = react.registerFunctionComponent(HelloGreg);
397+
398+
HelloGreg(Map props) {
399+
var content = ['Hello Greg!'];
400+
if (props['children'].isNotEmpty) {
401+
content = ['Hello ' + props['children'].join(' ') + '!'];
402+
}
403+
return react.Fragment({}, content);
404+
}
405+
396406
class _Component2TestComponent extends react.Component2 with react.TypedSnapshot<String> {
397407
get defaultProps => {'defaultProp': true};
398408

js_src/_dart_helpers.js

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,25 @@
11
/**
22
* react-dart JS interop helpers (used by react_client.dart and react_client/js_interop_helpers.dart)
33
*/
4+
45
/// Prefix to namespace react-dart symbols
5-
var _reactDartSymbolPrefix = 'react-dart.';
6+
const _reactDartSymbolPrefix = 'react-dart.';
7+
68
/// A global symbol to identify javascript objects owned by react-dart context,
79
/// in order to jsify and unjsify context objects correctly.
8-
var _reactDartContextSymbol = Symbol(_reactDartSymbolPrefix+'context');
10+
const _reactDartContextSymbol = Symbol(_reactDartSymbolPrefix + 'context');
911

1012
/// A JS side function to allow Dart to throw an error from JS in order to catch it Dart side.
1113
/// Used within Component2 error boundry methods to dartify the error argument.
1214
/// See: https://github.com/dart-lang/sdk/issues/36363
13-
function _throwErrorFromJS(error){
15+
function _throwErrorFromJS(error) {
1416
throw error;
1517
}
1618

19+
/// A JS variable that can be used with Fart interop in order to force returning a
20+
/// JavaScript `null`. This prevents dart2js from possibly converting Dart `null` into `undefined`.
21+
const _jsNull = null;
22+
1723
function _createReactDartComponentClass(dartInteropStatics, componentStatics, jsConfig) {
1824
class ReactDartComponent extends React.Component {
1925
constructor(props, context) {
@@ -131,7 +137,7 @@ function _createReactDartComponentClass2(dartInteropStatics, componentStatics, j
131137
}
132138

133139
// Delete methods that the user does not want to include (such as error boundary event).
134-
jsConfig.skipMethods.forEach((method) => {
140+
jsConfig.skipMethods.forEach(method => {
135141
if (ReactDartComponent2[method]) {
136142
delete ReactDartComponent2[method];
137143
} else {
@@ -165,4 +171,5 @@ export default {
165171
_createReactDartComponentClass2,
166172
_markChildValidated,
167173
_throwErrorFromJS,
168-
}
174+
_jsNull,
175+
};

lib/react.dart

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,15 @@ export 'package:react/src/context.dart';
1919
export 'package:react/src/prop_validator.dart';
2020
export 'package:react/react_client/react_interop.dart' show forwardRef, createRef;
2121

22+
typedef Error PropValidator<TProps>(TProps props, PropValidatorInfo info);
23+
24+
/// A React component declared using a function that takes in [props] and returns rendered output.
25+
///
26+
/// See <https://facebook.github.io/react/docs/components-and-props.html#functional-and-class-components>.
27+
typedef DartFunctionComponent = dynamic Function(Map props);
28+
2229
typedef T ComponentFactory<T extends Component>();
30+
2331
typedef ReactComponentFactoryProxy ComponentRegistrar(ComponentFactory componentFactory,
2432
[Iterable<String> skipMethods]);
2533

@@ -29,6 +37,9 @@ typedef ReactDartComponentFactoryProxy2 ComponentRegistrar2(
2937
Component2BridgeFactory bridgeFactory,
3038
});
3139

40+
typedef ReactDartFunctionComponentFactoryProxy FunctionComponentRegistrar(DartFunctionComponent componentFactory,
41+
{String displayName});
42+
3243
/// Fragment component that allows the wrapping of children without the necessity of using
3344
/// an element that adds an additional layer to the DOM (div, span, etc).
3445
///
@@ -1210,6 +1221,23 @@ mixin TypedSnapshot<TSnapshot> {
12101221

12111222
/// Creates a ReactJS virtual DOM instance (`ReactElement` on the client).
12121223
abstract class ReactComponentFactoryProxy implements Function {
1224+
/// The JS component factory used by this factory to build [ReactElement]s.
1225+
///
1226+
/// Deprecated: Use [React.createElement()] instead and pass in [type] as
1227+
/// the first argument, followed by `props` and `children`.
1228+
///
1229+
/// Before:
1230+
/// ```
1231+
/// YourFactoryProxy.reactComponentFactory(props, children);
1232+
/// ```
1233+
///
1234+
/// After:
1235+
/// ```
1236+
/// React.createElement(YourFactoryProxy.type, props, children);
1237+
/// ```
1238+
@Deprecated('6.0.0')
1239+
ReactJsComponentFactory reactComponentFactory;
1240+
12131241
/// The type of component created by this factory.
12141242
get type;
12151243

@@ -1790,6 +1818,32 @@ ComponentRegistrar2 registerComponent2 = (
17901818
throw new Exception('setClientConfiguration must be called before registerComponent.');
17911819
};
17921820

1821+
/// Registers [componentFactory] on client.
1822+
///
1823+
/// Example:
1824+
/// ```
1825+
/// var myFunctionComponent = registerFunctionComponent((Map props) {
1826+
/// return ['I am a function component', ...props.children];
1827+
/// });
1828+
/// ```
1829+
///
1830+
/// Example with display name:
1831+
/// ```
1832+
/// var myFunctionComponent = registerFunctionComponent((Map props) {
1833+
/// return ['I am a function component', ...props.children];
1834+
/// }, displayName: 'myFunctionComponent');
1835+
/// ```
1836+
/// or with an inferred name from the Dart function
1837+
/// ```
1838+
/// myDartFunctionComponent(Map props) {
1839+
/// return ['I am a function component', ...props.children];
1840+
/// }
1841+
/// var myFunctionComponent = registerFunctionComponent(myDartFunctionComponent);
1842+
/// ```
1843+
FunctionComponentRegistrar registerFunctionComponent = (DartFunctionComponent componentFactory, {String displayName}) {
1844+
throw new Exception('setClientConfiguration must be called before registerFunctionComponent.');
1845+
};
1846+
17931847
/// The HTML `<a>` [AnchorElement].
17941848
var a;
17951849

@@ -2594,9 +2648,15 @@ _createDOMComponents(creator) {
25942648
///
25952649
/// The arguments are assigned to global variables, and React DOM `Component`s are created by calling
25962650
/// [_createDOMComponents] with [domCreator].
2597-
void setReactConfiguration(domCreator, customRegisterComponent, {ComponentRegistrar2 customRegisterComponent2}) {
2651+
void setReactConfiguration(
2652+
domCreator,
2653+
customRegisterComponent, {
2654+
ComponentRegistrar2 customRegisterComponent2,
2655+
FunctionComponentRegistrar customRegisterFunctionComponent,
2656+
}) {
25982657
registerComponent = customRegisterComponent;
25992658
registerComponent2 = customRegisterComponent2;
2659+
registerFunctionComponent = customRegisterFunctionComponent;
26002660
// HTML Elements
26012661
_createDOMComponents(domCreator);
26022662
}

lib/react.js

Lines changed: 7 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/react.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)