Skip to content

Commit

Permalink
Merge pull request #616 from Workiva/function-components-wip
Browse files Browse the repository at this point in the history
RM-79371 Release over_react 3.8.0
  • Loading branch information
rmconsole3-wf authored Aug 10, 2020
2 parents 14b35b0 + aca6b05 commit d1904b8
Show file tree
Hide file tree
Showing 75 changed files with 7,560 additions and 492 deletions.
61 changes: 61 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,66 @@
# OverReact Changelog

## [3.8.0](https://github.com/Workiva/over_react/compare/3.7.0...3.8.0)

__New Features__

- 🎉 🎉 🎉 __Support for function components, memo and hooks!!!__ 🎉 🎉 🎉

Sooooo much work from so many amazing people made this possible, but to summarize:

- [#606] Add support for function components
- [#613] Add support for `memo` higher order component
- [#611] Hooks, hooks, and more hooks!
- useState
- useCallback
- useContext
- useEffect
- useLayoutEffect
- useReducer
- useRef
- useMemo
- useImperativeHandle
- useDebugValue

<p><br>It works like this...</p>

Define the component
```dart
mixin FancyBorderProps on UiProps {
String color;
}
UiFactory<FancyBorderProps> FancyBorder = uiFunction(
(props) {
// props is typed as a `FancyBorderProps`
// whatever you return here will be rendered
return (Dom.div()..className = 'fancy-border border-${props.color}')(
props.children,
);
},
$FancyBorderConfig, // ignore: undefined_identifier
);
```
Render the component _(exact same consumer API as a class-based component)_:
```dart
import 'package:over_react/over_react.dart';
import 'fancy_border.dart'; // Where your component is defined
main() {
final renderedWidget = (FancyBorder()..color = /* some color value */)(
// put some children here!
);
react_dom.render(renderedWidget, querySelector('#idOfSomeNodeInTheDom'));
}
```
__Other Changes__
- [#612] Deprecate `forwardRef` / add `uiForwardRef` as its replacement
## [3.7.0](https://github.com/Workiva/over_react/compare/3.6.0...3.7.0)
__Library Changes:__
Expand Down
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -846,6 +846,36 @@ that you get for free from OverReact, you're ready to start building your own cu
}
}
```

* #### Function Component Boilerplate

```dart
import 'package:over_react/over_react.dart';
part 'foo_component.over_react.g.dart';
UiFactory<FooProps> Foo = uiFunction(
(props) {
// Set default props using null-aware operators.
final isDisabled = props.isDisabled ?? false;
final items = props.items ?? [];
// Return the rendered component contents here.
// The `props` variable is typed; no need for string keys!
return Fragment()(
Dom.div()(items),
(Dom.button()..disabled = isDisabled)('Click me!'),
);
},
// The generated props config will match the factory name.
$FooConfig, // ignore: undefined_identifier
);

mixin FooProps on UiProps {
// Props go here, declared as fields:
bool isDisabled;
Iterable<String> items;
}
```

&nbsp;

Expand Down
60 changes: 49 additions & 11 deletions doc/new_boilerplate_migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -702,7 +702,7 @@ Most code within over_react has been updated to use this new boilerplate, includ
- The Redux sample todo app under [`app/over_react_redux/todo_client/`](../app/over_react_redux/todo_client)
## Function Component Boilerplate _(coming soon)_
## Function Component Boilerplate
### Function Component Constraints
Expand All @@ -727,22 +727,22 @@ import 'package:over_react/over_react.dart';
part 'foo.over_react.g.dart';
UiFactory<FooProps> Foo = uiFunctionComponent(
UiFactory<FooProps> Foo = uiFunction(
(props) {
return 'foo: ${props.foo}';
},
$fooPropsConfig, // ignore: undefined_identifier
$FooConfig, // ignore: undefined_identifier
);
mixin FooProps on UiProps {
String foo;
}
```

Here, `uiFunctionComponent` gets a generic parameter of `FooProps` inferred
Here, `uiFunction` gets a generic parameter of `FooProps` inferred
from the LHS typing, allowing props to be statically typed as `FooProps`.

The generated `$FooPropsConfig` is passed in as an argument, and serves
The generated `$FooConfig` is passed in as an argument, and serves
as the entrypoint to the generated code.

#### With Default Props
Expand All @@ -755,24 +755,37 @@ same behavior as `defaultProps`, but with the restriction that a given prop
__must either be nullable or have a default value, but not both__.

```dart
UiFactory<FooProps> Foo = uiFunctionComponent(
UiFactory<FooProps> Foo = uiFunction(
(props) {
final foo = props.foo ?? 'default foo value';
return 'foo: $foo';
},
$fooPropsConfig, // ignore: undefined_identifier
$FooConfig, // ignore: undefined_identifier
);
```

#### With UiProps

```dart
UiFactory<UiProps> Foo = uiFunction(
(props) {
return 'id: ${props.id}';
},
UiFactoryConfig(
displayName: 'Foo',
),
);
```

#### With propTypes

```dart
UiFactory<FooProps> Foo = uiFunctionComponent(
UiFactory<FooProps> Foo = uiFunction(
(props) {
return 'foo: ${props.foo}';
},
$fooPropsConfig, // ignore: undefined_identifier
$FooConfig, // ignore: undefined_identifier
getPropTypes: (keyFor) => {
keyFor((p) => p.foo): (props, info) {
if (props.foo == 'bar') {
Expand Down Expand Up @@ -803,20 +816,45 @@ UiFactory<FooProps> createFooHoc(UiFactory otherFactory) {
Object closureVariable;
// ...
final FooHoc = uiFunctionComponent<FooProps>(
UiFactory<FooProps> FooHoc = uiFunction(
(props) {
return otherFactory()(
Dom.div()('closureVariable: ${closureVariable}'),
Dom.div()('prop foo: ${props.foo}'),
);
},
$fooPropsConfig, // ignore: undefined_identifier
UiFactoryConfig(
displayName: 'FooHoc',
propsFactory: PropsFactory.fromUiFactory(Foo),
),
);
return FooHoc;
}
```

#### With forwardRef

```dart
mixin FooProps on UiProps {
Ref forwardedRef;
Function doSomething;
}
UiFactory<FooProps> Foo = uiForwardRef(
(props, ref) {
return Fragment()(
Dom.div()('Some text.'),
(Dom.button()
..ref = ref
..onClick = props.doSomething
)('Click me!'),
);
},
$FooConfig, // ignore: undefined_identifier
);
```

## Upgrading Existing Code

To update your repository to the new boilerplate, you can use
Expand Down
3 changes: 3 additions & 0 deletions example/builder/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import './src/basic.dart';
import './src/basic_library.dart';
import './src/generic_inheritance_sub.dart';
import './src/generic_inheritance_super.dart';
import './src/function_component.dart' as function;

main() {
setClientConfiguration();
Expand Down Expand Up @@ -52,6 +53,8 @@ main() {
..subProp = 'sub prop part of lib'
..superProp = 'super prop part of lib'
)(),
Dom.h3()('Function component:'),
function.functionComponentContent(),
Dom.h3()('getDefaultProps via component factories'),
componentConstructorsByName.keys.map((name) => Dom.div()(
'new $name()',
Expand Down
106 changes: 106 additions & 0 deletions example/builder/src/function_component.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// Copyright 2020 Workiva Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import 'package:over_react/over_react.dart';

// ignore_for_file: uri_has_not_been_generated
part 'function_component.over_react.g.dart';

mixin BasicProps on UiProps {
Ref forwardedRef;
String basicProp;
String basic1;
String basic2;
String basic3;
String basic4;
}

UiFactory<BasicProps> Basic = uiForwardRef(
(props, ref) {
return Fragment()(
Dom.div()('prop id: ${props.id}'),
Dom.div()('default prop testing: ${props.basicProp}'),
Dom.div()('default prop testing: ${props.basic1}'),
(Dom.div()..ref = ref)(
props.basic3, 'children: ${props.children}'),
);
},
$BasicConfig, // ignore: undefined_identifier
);

UiFactory<BasicProps> Simple = uiFunction(
(props) {
final basicProp = props.basicProp ?? 'basicProp';
final basic1 = props.basic1 ?? 'basic1';

return Fragment()(
Dom.div()('prop id: ${props.id}'),
Dom.div()('default prop testing: $basicProp'),
Dom.div()('default prop testing: $basic1'),
Dom.div()(null, props.basic4, 'children: ${props.children}'),
(Foo()..content = props.basic2)(),
);
},
$SimpleConfig, // ignore: undefined_identifier
);

mixin FooProps on UiProps {
String content;
}

UiFactory<FooProps> Foo = uiFunction(
(props) => Dom.div()('forwarded prop: ${props.content}'),
$FooConfig, // ignore: undefined_identifier
);

ReactElement functionComponentContent() {
GenericFactory(props) {
return Dom.div()('prop id: ${props.id}');
}

UiFactory<UiProps> genericFactory = uiFunction(
GenericFactory,
UiFactoryConfig(),
);

UiFactory<BasicProps> basicFactory = uiFunction(
(props) {
return Fragment()(
Dom.div()('prop id: ${props.id}'),
Dom.div()('prop basic1: ${props.basic1}'),
);
},
UiFactoryConfig(
propsFactory: PropsFactory.fromUiFactory(Basic),
displayName: 'basicFactory',
)
);

// Access the div element later using `divRef.current`.
Ref divRef = createRef();

return Fragment()(
(genericFactory()..id = '1')(),
(basicFactory()
..id = '2'
..basic1 = 'basic1 value')(),
(Basic()
..ref = divRef
..id = '3'
..basicProp = 'basicProp')(),
(Simple()
..id = '4'
..basic2 = 'basic2 value')(),
);
}
Loading

0 comments on commit d1904b8

Please sign in to comment.