Skip to content

Commit 05a0662

Browse files
Improve / add forwardRef examples
1 parent e269eb3 commit 05a0662

File tree

6 files changed

+401
-29
lines changed

6 files changed

+401
-29
lines changed

lib/src/component/ref_util.dart

Lines changed: 114 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -54,42 +54,127 @@ Ref<T> createRef<T>() {
5454

5555
/// Automatically passes a [Ref] through a component to one of its children.
5656
///
57-
/// __Example__:
57+
/// __Example 1:__ Forwarding refs to DOM components
58+
///
59+
/// ```dart
60+
/// import 'dart:html';
61+
/// import 'package:over_react/over_react.dart';
62+
/// import 'package:over_react/react_dom.dart' as react_dom;
63+
///
64+
/// // ---------- Component Definition ----------
65+
///
66+
/// final FancyButton = forwardRef<DomProps>((props, ref) {
67+
/// final classes = ClassNameBuilder.fromProps(props)
68+
/// ..add('FancyButton');
69+
///
70+
/// return (Dom.button()
71+
/// ..addProps(getPropsToForward(props, onlyCopyDomProps: true))
72+
/// ..className = classes.toClassName()
73+
/// ..ref = ref
74+
/// )('Click me!');
75+
/// })(Dom.button);
76+
///
77+
/// // ---------- Component Consumption ----------
78+
///
79+
/// void main() {
80+
/// setClientConfiguration();
81+
/// final ref = createRef<Element>();
5882
///
59-
/// UiFactory<DomProps> DivForwarded = forwardRef<DomProps>((props, ref) {
60-
/// return (Dom.div()
83+
/// react_dom.render(
84+
/// (FancyButton()
6185
/// ..ref = ref
62-
/// ..className = 'special-class'
63-
/// )(
64-
/// props.children
65-
/// );
66-
/// })(Dom.div);
86+
/// ..onClick = (_) {
87+
/// print(ref.current.outerHtml);
88+
/// }
89+
/// )(),
90+
/// querySelector('#idOfSomeNodeInTheDom')
91+
/// );
6792
///
68-
/// ___ OR ___
93+
/// // You can still get a ref directly to the DOM button:
94+
/// final buttonNode = ref.current;
95+
/// }
96+
/// ```
6997
///
70-
/// UiFactory<FooProps> FooForwarded = forwardRef<FooProps>((props, ref) {
71-
/// return (Foo()
72-
/// ..forwardedRef = ref
73-
/// )();
74-
/// })(Foo);
98+
/// __Example 2:__ Forwarding refs in higher-order components
7599
///
76-
/// @Factory()
77-
/// UiFactory<FooProps> Foo = _$Foo;
100+
/// ```dart
101+
/// import 'dart:html';
102+
/// import 'package:over_react/over_react.dart';
103+
/// import 'package:over_react/react_dom.dart' as react_dom;
78104
///
79-
/// @Props()
80-
/// class _$FooProps extends UiProps {
81-
/// Ref forwardedRef;
82-
/// }
105+
/// // ---------- Component Definitions ----------
83106
///
84-
/// @Component2()
85-
/// class FooComponent extends UiComponent2<FooProps> {
86-
/// @override
87-
/// render() {
88-
/// return (Dom.button()
89-
/// ..ref = props.forwardedRef
90-
/// )('Click this button');
91-
/// }
92-
/// }
107+
/// final FancyButton = forwardRef<DomProps>((props, ref) {
108+
/// final classes = ClassNameBuilder.fromProps(props)
109+
/// ..add('FancyButton');
110+
///
111+
/// return (Dom.button()
112+
/// ..addProps(getPropsToForward(props, onlyCopyDomProps: true))
113+
/// ..className = classes.toClassName()
114+
/// ..ref = ref
115+
/// )('Click me!');
116+
/// })(Dom.button);
117+
///
118+
/// final LogProps = forwardRef<LogPropsProps>((props, ref) {
119+
/// return (_LogProps()
120+
/// ..addProps(props)
121+
/// .._forwardedRef = ref
122+
/// )('Click me!');
123+
/// })(_LogProps);
124+
///
125+
/// @Factory()
126+
/// UiFactory<LogPropsProps> _LogProps = _$_LogProps;
127+
///
128+
/// @Props()
129+
/// class _$LogPropsProps extends UiProps {
130+
/// BuilderOnlyUiFactory<DomProps> builder;
131+
///
132+
/// // Private since we only use this to pass along the value of `ref` to
133+
/// // the return value of forwardRef.
134+
/// //
135+
/// // Consumers can set this private field value using the public `ref` setter.
136+
/// Ref _forwardedRef;
137+
/// }
138+
///
139+
/// @Component2()
140+
/// class LogPropsComponent extends UiComponent2<LogPropsProps> {
141+
/// @override
142+
/// void componentDidUpdate(Map prevProps, _, [__]) {
143+
/// print('old props: $prevProps');
144+
/// print('new props: $props');
145+
/// }
146+
///
147+
/// @override
148+
/// render() {
149+
/// return (props.builder()
150+
/// ..modifyProps(addUnconsumedDomProps)
151+
/// ..ref = props._forwardedRef
152+
/// )(props.children);
153+
/// }
154+
/// }
155+
///
156+
/// // ---------- Component Consumption ----------
157+
///
158+
/// void main() {
159+
/// setClientConfiguration();
160+
/// final ref = createRef<Element>();
161+
///
162+
/// react_dom.render(
163+
/// (LogProps()
164+
/// ..builder = FancyButton
165+
/// ..className = 'btn btn-primary'
166+
/// ..ref = ref
167+
/// ..onClick = (_) {
168+
/// print(ref.current.outerHtml);
169+
/// }
170+
/// )(),
171+
/// querySelector('#idOfSomeNodeInTheDom')
172+
/// );
173+
///
174+
/// // You can still get a ref directly to the DOM button:
175+
/// final buttonNode = ref.current;
176+
/// }
177+
/// ```
93178
///
94179
/// Learn more: <https://reactjs.org/docs/forwarding-refs.html>.
95180
UiFactory<TProps> Function(UiFactory<TProps>) forwardRef<TProps extends UiProps>(

web/component2/index.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import 'src/demos.dart';
77

88
void main() {
99
setClientConfiguration();
10+
final fancyButtonNodeRef = createRef<Element>();
1011

1112
react_dom.render(
1213
buttonExamplesDemo(), querySelector('$demoMountNodeSelectorPrefix--button'));
@@ -26,6 +27,18 @@ void main() {
2627
react_dom.render(
2728
radioToggleButtonDemo(), querySelector('$demoMountNodeSelectorPrefix--radio-toggle'));
2829

30+
react_dom.render(
31+
(LogProps()
32+
..builder = FancyButton
33+
..className = 'btn btn-primary'
34+
..ref = fancyButtonNodeRef
35+
..onClick = (_) {
36+
print(fancyButtonNodeRef.current.outerHtml);
37+
}
38+
)(),
39+
querySelector('$demoMountNodeSelectorPrefix--forwardRef'),
40+
);
41+
2942
react_dom.render(
3043
(ErrorBoundary()
3144
..onComponentDidCatch = (error, info) {

web/component2/index.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,12 @@ <h2>ToggleButton (Radio)</h2>
5151
around with the component rendered below.</p>
5252
<div class="component-demo__mount--radio-toggle"></div>
5353
</div>
54+
<div class="col-xs-12 col-lg-6 p-3">
55+
<h2>ForwardRef</h2>
56+
<p>Modify the source of <code>/web/component2/src/demos/forward_ref.dart</code> to play
57+
around with the component rendered below.</p>
58+
<div class="component-demo__mount--forwardRef"></div>
59+
</div>
5460
<div class="col-xs-12 col-lg-6 p-3">
5561
<h2>Component That Throws A Caught Error</h2>
5662
<p><strong>(Default ErrorBoundary Component)</strong></p>

web/component2/src/demos.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export 'demos/button/button-active.dart';
99
export 'demos/button/button-disabled.dart';
1010

1111
export 'demos/custom_error_boundary.dart';
12+
export 'demos/forward_ref.dart';
1213

1314
export '../src/demo_components/prop_validation_wrap.dart';
1415

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import 'package:over_react/over_react.dart';
2+
3+
// ignore: uri_has_not_been_generated
4+
part 'forward_ref.over_react.g.dart';
5+
6+
final FancyButton = forwardRef<DomProps>((props, ref) {
7+
final classes = ClassNameBuilder.fromProps(props)
8+
..add('FancyButton');
9+
10+
return (Dom.button()
11+
..addProps(getPropsToForward(props, onlyCopyDomProps: true))
12+
..className = classes.toClassName()
13+
..ref = ref
14+
)('Click me!');
15+
}, displayName: 'FancyButton')(Dom.button);
16+
17+
final LogProps = forwardRef<LogPropsProps>((props, ref) {
18+
return (_LogProps()
19+
..addProps(props)
20+
.._forwardedRef = ref
21+
)('Click me!');
22+
}, displayName: 'LogProps')(_LogProps);
23+
24+
@Factory()
25+
UiFactory<LogPropsProps> _LogProps = _$_LogProps;
26+
27+
@Props()
28+
class _$LogPropsProps extends UiProps {
29+
BuilderOnlyUiFactory<DomProps> builder;
30+
31+
// Private since we only use this to pass along the value of `ref` to
32+
// the return value of forwardRef.
33+
//
34+
// Consumers can set this private field value using the public `ref` setter.
35+
Ref _forwardedRef;
36+
}
37+
38+
@Component2()
39+
class LogPropsComponent extends UiComponent2<LogPropsProps> {
40+
@override
41+
void componentDidUpdate(Map prevProps, _, [__]) {
42+
print('old props: $prevProps');
43+
print('new props: $props');
44+
}
45+
46+
@override
47+
render() {
48+
return (props.builder()
49+
..modifyProps(addUnconsumedDomProps)
50+
..ref = props._forwardedRef
51+
)(props.children);
52+
}
53+
}
54+
55+
//class _LogProps extends react.Component2 {
56+
// @override
57+
// void componentDidUpdate(Map prevProps, _, [__]) {
58+
// print('old props: $prevProps');
59+
// print('new props: ${this.props}');
60+
// }
61+
//
62+
// @override
63+
// render() {
64+
// final propsToForward = {...props}..remove('forwardedRef');
65+
//
66+
// // Assign the custom prop `forwardedRef` as a ref on the component passed in via `props.component`
67+
// return props['component']({...propsToForward, 'ref': props['forwardedRef']}, props['children']);
68+
// }
69+
//}
70+
//final _logPropsHoc = react.registerComponent2(() => _LogProps());
71+
//
72+
//final LogProps = react.forwardRef((props, ref) {
73+
// // Note the second param "ref" provided by react.forwardRef.
74+
// // We can pass it along to LogProps as a regular prop, e.g. "forwardedRef"
75+
// // And it can then be attached to the Component.
76+
// return _logPropsHoc({...props, 'forwardedRef': ref});
77+
// // Optional: Make the displayName more useful for the React dev tools.
78+
// // See: https://reactjs.org/docs/forwarding-refs.html#displaying-a-custom-name-in-devtools
79+
//}, displayName: 'LogProps(${_logPropsHoc.type.displayName})');
80+
//
81+
//final LogProps = forwardRef<LogPropsProps>((props, ref) {
82+
// return (_LogProps()
83+
// ..addProps(props)
84+
// .._forwardedRef = ref
85+
// )(props.children);
86+
//})(_LogProps);

0 commit comments

Comments
 (0)