Skip to content

Commit 5167432

Browse files
Merge pull request #106 from aaronlademann-wf/over_react-strong-mode-tweaks
Make adjustments necessary to support strong-mode compliance for over_react
2 parents 89c049b + 9f61f6e commit 5167432

File tree

5 files changed

+83
-32
lines changed

5 files changed

+83
-32
lines changed

lib/react.dart

Lines changed: 68 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,56 @@
55
/// A Dart library for building UI using ReactJS.
66
library react;
77

8-
/// Top-level ReactJS [Component class](https://facebook.github.io/react/docs/top-level-api.html#react.component)
9-
/// which provides the [ReactJS Component API](https://facebook.github.io/react/docs/component-api.html)
8+
import 'package:react/src/typedefs.dart';
9+
10+
/// Top-level ReactJS [Component class](https://facebook.github.io/react/docs/react-component.html)
11+
/// which provides the [ReactJS Component API](https://facebook.github.io/react/docs/react-component.html#reference)
1012
abstract class Component {
11-
/// ReactJS `Component` props.
12-
Map props;
13+
/// A private field that backs [props], which is exposed via getter/setter so
14+
/// it can be overridden in strong mode.
15+
///
16+
/// Necessary since the `@virtual` annotation within the meta package
17+
/// [doesn't work for overriding fields](https://github.com/dart-lang/sdk/issues/27452).
18+
///
19+
/// TODO: Switch back to a plain field once this issue is fixed.
20+
Map _props;
21+
22+
/// A private field that backs [state], which is exposed via getter/setter so
23+
/// it can be overridden in strong mode.
24+
///
25+
/// Necessary since the `@virtual` annotation within the meta package
26+
/// [doesn't work for overriding fields](https://github.com/dart-lang/sdk/issues/27452).
27+
///
28+
/// TODO: Switch back to a plain field once this issue is fixed.
29+
Map _state = {};
1330

14-
/// Provides access to the underlying DOM representation of the [render]ed `Component`.
15-
dynamic ref;
31+
/// A private field that backs [ref], which is exposed via getter/setter so
32+
/// it can be overridden in strong mode.
33+
///
34+
/// Necessary since the `@virtual` annotation within the meta package
35+
/// [doesn't work for overriding fields](https://github.com/dart-lang/sdk/issues/27452).
36+
///
37+
/// TODO: Switch back to a plain field once this issue is fixed.
38+
Ref _ref;
39+
40+
/// ReactJS [Component] props.
41+
///
42+
/// Related: [state]
43+
Map get props => _props;
44+
set props(Map value) => _props = value;
45+
46+
/// ReactJS [Component] state.
47+
///
48+
/// Related: [props]
49+
Map get state => _state;
50+
set state(Map value) => _state = value;
51+
52+
/// A function that returns a component reference:
53+
///
54+
/// * [Component] if it is a Dart component.
55+
/// * `Element` _(DOM node)_ if it is a React DOM component.
56+
Ref get ref => _ref;
57+
set ref(Ref value) => _ref = value;
1658

1759
dynamic _jsRedraw;
1860

@@ -32,14 +74,14 @@ abstract class Component {
3274
/// instance of this `Component` returned by [render].
3375
dynamic get jsThis => _jsThis;
3476

35-
/// Allows the [ReactJS `displayName` property](https://facebook.github.io/react/docs/component-specs.html#displayname)
77+
/// Allows the [ReactJS `displayName` property](https://facebook.github.io/react/docs/react-component.html#displayname)
3678
/// to be set for debugging purposes.
3779
String get displayName => runtimeType.toString();
3880

3981
/// Bind the value of input to [state[key]].
4082
bind(key) => [state[key], (value) => setState({key: value})];
4183

42-
initComponentInternal(props, _jsRedraw, [ref, _jsThis]) {
84+
initComponentInternal(props, _jsRedraw, [Ref ref, _jsThis]) {
4385
this._jsRedraw = _jsRedraw;
4486
this.ref = ref;
4587
this._jsThis = _jsThis;
@@ -57,9 +99,6 @@ abstract class Component {
5799
transferComponentState();
58100
}
59101

60-
/// ReactJS `Component` state.
61-
Map state = {};
62-
63102
/// Private reference to the value of [state] from the previous render cycle.
64103
///
65104
/// Useful for ReactJS lifecycle methods [shouldComponentUpdate], [componentWillUpdate] and [componentDidUpdate].
@@ -98,7 +137,7 @@ abstract class Component {
98137
///
99138
/// Optionally accepts a callback that gets called after the component updates.
100139
///
101-
/// [A.k.a "forceUpdate"](https://facebook.github.io/react/docs/component-api.html#forceupdate)
140+
/// [A.k.a "forceUpdate"](https://facebook.github.io/react/docs/react-component.html#forceupdate)
102141
void redraw([callback()]) {
103142
setState({}, callback);
104143
}
@@ -109,7 +148,7 @@ abstract class Component {
109148
///
110149
/// Also allows [newState] to be used as a transactional `setState` callback.
111150
///
112-
/// See: <https://facebook.github.io/react/docs/component-api.html#setstate>
151+
/// See: <https://facebook.github.io/react/docs/react-component.html#setstate>
113152
void setState(dynamic newState, [callback()]) {
114153
if (newState is Map) {
115154
_nextState.addAll(newState);
@@ -128,7 +167,7 @@ abstract class Component {
128167
///
129168
/// Optionally accepts a callback that gets called after the component updates.
130169
///
131-
/// See: <https://facebook.github.io/react/docs/component-api.html#replacestate>
170+
/// See: <https://facebook.github.io/react/docs/react-component.html#setstate>
132171
void replaceState(Map newState, [callback()]) {
133172
Map nextState = newState == null ? {} : new Map.from(newState);
134173
_nextState = nextState;
@@ -143,7 +182,7 @@ abstract class Component {
143182
/// If you call [setState] within this method, [render] will see the updated state and will be executed only once
144183
/// despite the [state] value change.
145184
///
146-
/// See: <https://facebook.github.io/react/docs/component-specs.html#mounting-componentwillmount>
185+
/// See: <https://facebook.github.io/react/docs/react-component.html#mounting-componentwillmount>
147186
void componentWillMount() {}
148187

149188
/// ReactJS lifecycle method that is invoked once, only on the client _(not on the server)_, immediately after the
@@ -153,7 +192,7 @@ abstract class Component {
153192
///
154193
/// The [componentDidMount] method of child `Component`s is invoked _before_ that of parent `Component`.
155194
///
156-
/// See: <https://facebook.github.io/react/docs/component-specs.html#mounting-componentdidmount>
195+
/// See: <https://facebook.github.io/react/docs/react-component.html#mounting-componentdidmount>
157196
void componentDidMount() {}
158197

159198
/// ReactJS lifecycle method that is invoked when a `Component` is receiving [newProps].
@@ -165,16 +204,16 @@ abstract class Component {
165204
///
166205
/// Calling [setState] within this function will not trigger an additional [render].
167206
///
168-
/// See: <https://facebook.github.io/react/docs/component-specs.html#updating-componentwillreceiveprops>
169-
void componentWillReceiveProps(newProps) {}
207+
/// See: <https://facebook.github.io/react/docs/react-component.html#updating-componentwillreceiveprops>
208+
void componentWillReceiveProps(Map newProps) {}
170209

171210
/// ReactJS lifecycle method that is invoked before rendering when [nextProps] or [nextState] are being received.
172211
///
173212
/// Use this as an opportunity to return false when you're certain that the transition to the new props and state
174213
/// will not require a component update.
175214
///
176-
/// See: <https://facebook.github.io/react/docs/component-specs.html#updating-shouldcomponentupdate>
177-
bool shouldComponentUpdate(nextProps, nextState) => true;
215+
/// See: <https://facebook.github.io/react/docs/react-component.html#updating-shouldcomponentupdate>
216+
bool shouldComponentUpdate(Map nextProps, Map nextState) => true;
178217

179218
/// ReactJS lifecycle method that is invoked immediately before rendering when [nextProps] or [nextState] are being
180219
/// received.
@@ -183,8 +222,8 @@ abstract class Component {
183222
///
184223
/// Use this as an opportunity to perform preparation before an update occurs.
185224
///
186-
/// See: <https://facebook.github.io/react/docs/component-specs.html#updating-componentwillupdate>
187-
void componentWillUpdate(nextProps, nextState) {}
225+
/// See: <https://facebook.github.io/react/docs/react-component.html#updating-componentwillupdate>
226+
void componentWillUpdate(Map nextProps, Map nextState) {}
188227

189228
/// ReactJS lifecycle method that is invoked immediately after the `Component`'s updates are flushed to the DOM.
190229
///
@@ -193,20 +232,20 @@ abstract class Component {
193232
/// Use this as an opportunity to operate on the [rootNode] (DOM) when the `Component` has been updated as a result
194233
/// of the values of [prevProps] / [prevState].
195234
///
196-
/// See: <https://facebook.github.io/react/docs/component-specs.html#updating-componentdidupdate>
197-
void componentDidUpdate(prevProps, prevState) {}
235+
/// See: <https://facebook.github.io/react/docs/react-component.html#updating-componentdidupdate>
236+
void componentDidUpdate(Map prevProps, Map prevState) {}
198237

199238
/// ReactJS lifecycle method that is invoked immediately before a `Component` is unmounted from the DOM.
200239
///
201240
/// Perform any necessary cleanup in this method, such as invalidating timers or cleaning up any DOM [Element]s that
202241
/// were created in [componentDidMount].
203242
///
204-
/// See: <https://facebook.github.io/react/docs/component-specs.html#unmounting-componentwillunmount>
243+
/// See: <https://facebook.github.io/react/docs/react-component.html#unmounting-componentwillunmount>
205244
void componentWillUnmount() {}
206245

207246
/// Invoked once before the `Component` is mounted. The return value will be used as the initial value of [state].
208247
///
209-
/// See: <https://facebook.github.io/react/docs/component-specs.html#getinitialstate>
248+
/// See: <https://facebook.github.io/react/docs/react-component.html#getinitialstate>
210249
Map getInitialState() => {};
211250

212251
/// Invoked once and cached when [reactComponentClass] is called. Values in the mapping will be set on [props]
@@ -215,7 +254,7 @@ abstract class Component {
215254
/// This method is invoked before any instances are created and thus cannot rely on [props]. In addition, be aware
216255
/// that any complex objects returned by `getDefaultProps` will be shared across instances, not copied.
217256
///
218-
/// See: <https://facebook.github.io/react/docs/component-specs.html#getdefaultprops>
257+
/// See: <https://facebook.github.io/react/docs/react-component.html#getdefaultprops>
219258
Map getDefaultProps() => {};
220259

221260
/// __Required.__
@@ -224,13 +263,13 @@ abstract class Component {
224263
/// be either a virtual representation of a native DOM component (such as [DivElement]) or another composite
225264
/// `Component` that you've defined yourself.
226265
///
227-
/// See: <https://facebook.github.io/react/docs/component-specs.html#render>
266+
/// See: <https://facebook.github.io/react/docs/react-component.html#render>
228267
dynamic render();
229268
}
230269

231270
/// Typedef of a transactional [Component.setState] callback.
232271
///
233-
/// See: <https://facebook.github.io/react/docs/component-api.html#setstate>
272+
/// See: <https://facebook.github.io/react/docs/react-component.html#setstate>
234273
typedef Map _TransactionalSetStateCallback(Map prevState, Map props);
235274

236275

lib/react_client.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import 'package:react/react_client/react_interop.dart';
1616
import "package:react/react_dom.dart";
1717
import "package:react/react_dom_server.dart";
1818
import "package:react/src/react_client/synthetic_event_wrappers.dart" as events;
19+
import 'package:react/src/typedefs.dart';
1920

2021
export 'package:react/react_client/react_interop.dart' show ReactElement, ReactJsComponentFactory;
2122

@@ -167,7 +168,7 @@ final ReactDartInteropStatics _dartInteropStatics = (() {
167168
}
168169
};
169170

170-
var getRef = (name) {
171+
Ref getRef = (name) {
171172
var ref = getProperty(jsThis.refs, name);
172173
if (ref == null) return null;
173174
if (ref is Element) return ref;

lib/react_test.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
library react_test;
66

77
import 'package:react/react.dart';
8+
import 'package:react/src/typedefs.dart';
89

910
class MarkupDescription {
1011
final String tag;
@@ -29,7 +30,7 @@ _reactDom(String name) {
2930
};
3031
}
3132

32-
initializeComponent(Component component, [Map props = const {}, List children, redraw, ref]) {
33+
initializeComponent(Component component, [Map props = const {}, List children, redraw, Ref ref]) {
3334
if (redraw == null) redraw = () {};
3435
var extendedProps = new Map.from(component.getDefaultProps())
3536
..addAll(props);

lib/src/typedefs.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
library react.typedefs;
2+
3+
/// Typedef for `react.Component.ref`, which should return one of the following specified by the provided [ref]:
4+
///
5+
/// * `react.Component` if it is a Dart component.
6+
/// * `Element` _(DOM node)_ if it is a React DOM component.
7+
typedef dynamic Ref(String ref);

test/child_key_warning_test.dart

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ int _nextFactoryId = 0;
1313
///
1414
/// This prevents React JS from not printing key warnings it deems as "duplicates".
1515
void renderWithUniqueOwnerName(ReactElement render()) {
16-
ReactDartComponentFactoryProxy factory = react.registerComponent(() => new _OwnerHelperComponent());
16+
final factory =
17+
react.registerComponent(() => new _OwnerHelperComponent()) as ReactDartComponentFactoryProxy;
1718
factory.reactClass.displayName = 'OwnerHelperComponent_$_nextFactoryId';
1819
_nextFactoryId++;
1920

@@ -128,9 +129,11 @@ void main() {
128129

129130
Function CustomComponent = react.registerComponent(() => new _CustomComponent());
130131
class _CustomComponent extends react.Component {
132+
@override
131133
render() => react.div({}, []);
132134
}
133135

134136
class _OwnerHelperComponent extends react.Component {
137+
@override
135138
render() => props['render']();
136139
}

0 commit comments

Comments
 (0)