{"id":13471466,"url":"https://workiva.github.io/over_react/","last_synced_at":"2025-03-26T13:31:11.934Z","repository":{"id":39743547,"uuid":"70642157","full_name":"Workiva/over_react","owner":"Workiva","description":"A library for building statically-typed React UI components using Dart.","archived":false,"fork":false,"pushed_at":"2024-10-24T22:02:52.000Z","size":38945,"stargazers_count":428,"open_issues_count":49,"forks_count":58,"subscribers_count":39,"default_branch":"master","last_synced_at":"2024-10-30T02:59:42.355Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Workiva.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-10-11T22:56:57.000Z","updated_at":"2024-10-24T22:02:56.000Z","dependencies_parsed_at":"2023-02-18T10:32:10.520Z","dependency_job_id":"e729a3d9-2acf-4bc0-a693-4bd81d226985","html_url":"https://github.com/Workiva/over_react","commit_stats":null,"previous_names":[],"tags_count":195,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Workiva%2Fover_react","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Workiva%2Fover_react/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Workiva%2Fover_react/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Workiva%2Fover_react/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Workiva","download_url":"https://codeload.github.com/Workiva/over_react/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245662824,"owners_count":20652087,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T16:00:45.414Z","updated_at":"2025-03-26T13:31:09.163Z","avatar_url":"https://github.com/Workiva.png","language":"Dart","readme":"# OverReact\n\nA library for building statically-typed React UI components using Dart.\n\nThis library also exposes _OverReact Redux_, which has [its own documentation](doc/over_react_redux_documentation.md).\n\n[![Pub](https://img.shields.io/pub/v/over_react.svg)](https://pub.dev/packages/over_react)\n[![Documentation](https://img.shields.io/badge/docs-over_react-blue.svg)](https://pub.dev/documentation/over_react/latest/)\n[![OverReact Analyzer Plugin (beta)](https://img.shields.io/badge/docs-analyzer_plugin_(beta)-ff69b4.svg)](https://workiva.github.io/over_react/analyzer_plugin/)\n[![Join the gitter chat](https://badges.gitter.im/over_react/Lobby.svg)][gitter-chat]\n\n[![Dart CI](https://github.com/Workiva/over_react/workflows/Dart%20CI/badge.svg?branch=master)](https://github.com/Workiva/over_react/actions?query=workflow%3A%22Dart+CI%22+branch%3Amaster)\n\n---\n\n* __[Migration Guides](#migration-guides)__\n* __[Using OverReact](#using-overreact)__\n    * [Running tests in your project](#running-unit-tests-in-your-project)\n        * [OverReact Component Unit Test Examples](#overreact-component-unit-test-examples)\n* __[Anatomy of an OverReact component](#anatomy-of-an-overreact-component)__\n    * [UiFactory](#uifactory)\n    * [UiProps](#uiprops)\n    * [UiState](#uistate)\n    * [UiComponent2](#uicomponent2)\n* __[Fluent-style component consumption](#fluent-style-component-consumption)__\n* __[DOM components and props](#dom-components-and-props)__\n* __[Component Formatting](#component-formatting)__\n* __[Building custom components](#building-custom-components)__\n    * __[Component Boilerplates](#component-boilerplate-templates)__\n    * __[Component Best Practices](#component-best-practices)__\n* __[Contributing](#contributing)__\n\n[](#__START_EMBEDDED_README__)\n\n## Migration Guides\n\n### Null safety\nover_react 5.0.0 introduces support for null safety. \n\nNow, you can declare non-nullable required props, using the `late` keyword. See [the docs null safety and required props](./doc/null_safety_and_required_props.md) for more information.\n\nTo migrate components to null safety, see our [__null safety migration guide__](doc/null_safety/null_safe_migration.md).\n\n### More Migration Guides\n- [__New component boilerplate__](doc/new_boilerplate_migration.md): How to update to the mixin-based (`mixin MyProps on UiProps {}`) over_react component declaration syntax.\n- [__UiComponent2__](doc/ui_component2_transition.md): How to move off the deprecated `UiComponent` base class and onto `UiComponent2`. \n- [__BuiltRedux to Redux__](doc/built_redux_to_redux.md): How to transition to OverReact Redux from BuiltRedux.\n- [__Flux to Redux__](doc/flux_to_redux.md): A guide to how to transition from w_flux to OverReact Redux. This guide also introduces a new architecture, Influx, that can be used for incremental refactors.\n- [__Dart2 Migration__](doc/dart2_migration.md): Documentation on the Dart 2 builder updates and how to transition componentry to Dart 2.\n\n\u0026nbsp;\n\u0026nbsp;\n\n## Using OverReact\n\n\u003e __Prerequisites__\n\u003e\n\u003e - __Familiarize yourself with React JS__\n\u003e\n\u003e   Since OverReact is built atop React JS, we strongly encourage you to gain familiarity with it by reading some [React JS tutorials][react-js-tutorial] first.\n\u003e\n\u003e - __Familiarize yourself with Dart Web applications__\n\u003e\n\u003e   If you have never built a Web application in Dart, we strongly encourage you to gain familiarity with the core terminology, tools and boilerplate necessary to serve an application locally using Dart. [Dart has fantastic documentation and tutorials to get you started](https://dart.dev/tutorials/web/get-started).\n\n1. Add the `over_react` package as a dependency in your `pubspec.yaml`.\n\n    ```yaml\n    dependencies:\n      over_react: ^4.0.0\n    ```\n   \n1. Enable the **[OverReact Analyzer Plugin (beta)](tools/analyzer_plugin/)**, which has many lints and assists to make authoring OverReact components easier!\n\n1. Include the native JavaScript `react` and `react_dom` libraries in your app’s `index.html` file,\nand add an HTML element with a unique identifier where you’ll mount your OverReact UI component(s).\n\n    ```html\n    \u003chtml\u003e\n      \u003chead\u003e\n        \u003c!-- ... --\u003e\n      \u003c/head\u003e\n      \u003cbody\u003e\n        \u003cdiv id=\"react_mount_point\"\u003e\n          // OverReact component render() output will show up here.\n        \u003c/div\u003e\n\n        \u003cscript src=\"packages/react/react.js\"\u003e\u003c/script\u003e\n        \u003cscript src=\"packages/react/react_dom.js\"\u003e\u003c/script\u003e\n\n        \u003c!-- NOTE: \"index\" should correspond to the\n             name of the `.dart` file that contains your `main()` entrypoint. --\u003e\n        \u003cscript type=\"application/javascript\" defer src=\"index.dart.js\"\u003e\u003c/script\u003e\n      \u003c/body\u003e\n    \u003c/html\u003e\n    ```\n\n    \u003e __Note:__ When serving your application in production, use `packages/react/react_with_react_dom_prod.js`\n    file instead of the un-minified `react.js` / `react_dom.js` files shown in the example above.\n\n1. Import the `over_react` and `react_dom` libraries into `index.dart`. Then [build some components](#building-custom-components) and mount / render a React tree within the HTML element you created in the previous step by calling `react_dom.render()` within the `main()` entrypoint of your Dart application.\n\n    \u003e Be sure to namespace the `react_dom.dart` import as `react_dom` to avoid collisions with `UiComponent.render`\n      when [creating custom components](#building-custom-components).\n\n    ```dart\n    import 'dart:html';\n    import 'package:over_react/react_dom.dart' as react_dom;\n    import 'package:over_react/over_react.dart';\n\n    // Example of where the `Foo` component might be exported from\n    import 'package:your_package_name/foo.dart';\n\n    main() {\n      // Mount / render your component/application.\n      react_dom.render(\n        Foo()(),\n        querySelector('#react_mount_point'),\n      );\n    }\n    ```\n\n1. Run `webdev serve` in the root of your Dart project.\n\n\u003e **Note:** If you're not using [the latest component boilerplate](doc/new_boilerplate_migration.md), you'll have to restart your analysis server in your IDE for the built types to resolve properly after the build completes. Unfortunately, this is a [known limitation in the analysis server at this time](https://github.com/dart-lang/sdk/issues/34344).\n\u003e\n\u003e [__Migrate your components to the latest component boilerplate to never worry about this again!__](doc/new_boilerplate_migration.md)\n\n\u0026nbsp;\n\n### Running unit tests in your project\n\nWhen running unit tests on code that uses the over_react [builder] _(or any code that imports `over_react`)_,\n__you must run your tests using the `build_runner` package__.\n\n\u003e **Warning:** Do **_not_** run tests via `pub run build_runner test` in a package while another instance of `build_runner`\n(e.g. `pub run build_runner serve`) is running in that same package. [This workflow is unsupported by build_runner](https://github.com/dart-lang/build/issues/352#issuecomment-461554316)\n\n1. Run tests using the `build_runner` package, and specify the platform to be a browser platform. Example:\n\n    ```bash\n    $ pub run build_runner test -- -p chrome test/your_test_file.dart\n    ```\n\n#### OverReact Component Unit Test Examples\n\nBelow are links to a UI component from our example [\"Todo App\"](https://github.com/Workiva/over_react/tree/master/app/over_react_redux/todo_client), and its analogous tests that we've written for components we use in . We utilize the utilities found in our [`over_react_test` library](https://github.com/Workiva/over_react_test).\n\n* __`TodoListItem`__\n    * [Component Definition](app/over_react_redux/todo_client/lib/src/components/todo_list_item.dart)\n    * [Component Unit Tests](app/over_react_redux/todo_client/test/unit/browser/components/todo_list_item_test.dart)\n    * [\"Connected\" OverReact Redux Unit Tests](app/over_react_redux/todo_client/test/unit/browser/components/connected_todo_list_item_test.dart)\n\n\u0026nbsp;\n\u0026nbsp;\n\n\n\n## Anatomy of an OverReact component\n\n\u003e __If you are not familiar with React JS__\n\u003e\n\u003e Since OverReact is built atop React JS, we strongly encourage you to gain\n\u003e familiarity with it by reading this [React JS tutorial][react-js-tutorial] first.\n\nThe `over_react` library functions as an additional \"layer\" atop the [Dart react package][react-dart]\nwhich handles the underlying JS interop that wraps around [React JS][react-js].\n\nThe library strives to maintain a 1:1 relationship with the React JS component class and API.\nTo do that, an OverReact component is comprised of four core pieces that are each wired up\nvia our [builder].\n\n1. [UiFactory](#uifactory)\n2. [UiProps](#uiprops)\n3. component, either a:\n    1. function component [uiFunction](#uifunction-function-components)\n    2. class component [UiComponent2](#uicomponent2-class-based-components) (and optionally a [UiState](#uistate))  \n\n\u0026nbsp;\n\n### UiFactory\n\n__`UiFactory` is a function__ that returns a new instance of a component's [`UiProps`](#uiprops) class.\n\n```dart\n// Class component\nUiFactory\u003cFooProps\u003e Foo = castUiFactory(_$Foo);\n\n// Function component\nUiFactory\u003cFooProps\u003e Foo = uiFunction((props) { /*...*/ }, _$FooConfig);\n```\n\n* This factory is __the entry-point__ to consuming any OverReact component.\n* The `UiProps` instance it returns can be used [as a component builder](#uiprops-as-a-builder),\nor [as a typed view into an existing props map](#uiprops-as-a-map).\n* `castUiFactory` is necessary to prevent implicit cast analysis warnings before code generation has been run.\n\n\u0026nbsp;\n\n### UiProps\n\n__`UiProps` is a `Map` class__ that adds statically-typed getters and setters for each React component prop.\nIt can also be invoked as a function, serving [as a builder](#uiprops-as-a-builder) for its analogous component.\n\n```dart\nmixin FooProps on UiProps {\n  // ... the props for your component go here\n  String? bar;\n  bool? baz;\n  List\u003cint\u003e? bizzles;\n}\n```\n* * **Note:** The [builder] generates a class with getters and setters overriding the fields you declare in your mixin, but you don't need to worry about that generated class. To use props from another mixin, simply mix it in! See [_\"With other mixins\"_](#with-other-mixins) below for more information.\n\n\u0026nbsp;\n\n#### With other mixins\n\n__To compose props mixin classes__, create a class alias that uses `UiProps` as the base and mix in multiple props mixins. The generated props implementation will then use it as the base class and implement the generated version of those props mixins.\n\n```dart\nUiFactory\u003cFooProps\u003e Foo = castUiFactory(_$Foo); // ignore: undefined_identifier\n\nmixin FooPropsMixin on UiProps {\n  String? bar;\n  bool? baz;\n  List\u003cint\u003e? bizzles;\n}\n\nclass FooProps = UiProps with FooPropsMixin, BarPropsMixin;\n\nclass FooComponent extends UiComponent2\u003cFooProps\u003e {\n  // ...\n}\n```\n\n##### Composition\nThe use-case for composing multiple props mixins into a single component props class is typically a component that renders another component, and therefore needs to expose the prop interface of that child component which will get forwarded via [`addUnconsumedProps`](https://pub.dev/documentation/over_react/3.1.0/over_react/UiComponent2/addUnconsumedProps.html). \n\n[__Check out an example of props mixin component composition here__](doc/props_mixin_component_composition.md) \n\n\u0026nbsp;\n\n#### UiProps as a Map\n\n\u003e [!WARNING]\n\u003e Directly reading `late` required props on arbitrary maps is unsafe.\n\u003e \n\u003e See the docs on [Unsafe Required Prop Reads](doc/null_safety_and_required_props.md#unsafe-required-prop-reads) \n\u003e for more information and for instructions on how to read these props safely.\n\n```dart\nUiFactory\u003cFooProps\u003e Foo = castUiFactory(_$Foo); // ignore: undefined_identifier\n\nmixin FooProps on UiProps {\n  String? color;\n}\n\nclass FooComponent extends UiComponent2\u003cFooProps\u003e {\n  // ...\n}\n\nvoid bar() {\n  FooProps props = Foo();\n\n  props.color = '#66cc00';\n\n  print(props.color); // #66cc00\n  print(props);       // {FooProps.color: #66cc00}\n}\n\n/// You can also use the factory to create a UiProps instance\n/// backed by an existing Map.\nvoid baz() {\n  Map existingMap = {'FooProps.color': '#0094ff'};\n\n  FooProps props = Foo(existingMap);\n\n  print(props.color); // #0094ff\n}\n```\n\n\u0026nbsp;\n\n#### UiProps as a builder\n\n```dart\nUiFactory\u003cFooProps\u003e Foo = castUiFactory(_$Foo); // ignore: undefined_identifier\n\nmixin FooProps on UiProps {\n  String? color;\n}\n\nclass FooComponent extends UiComponent2\u003cFooProps\u003e {\n  ReactElement bar() {\n    // Create a UiProps instance to serve as a builder\n    FooProps builder = Foo();\n\n    // Set some prop values\n    builder\n      ..id = 'the_best_foo'\n      ..color = '#ee2724';\n\n    // Invoke as a function with the desired children\n    // to return a new instance of the component.\n    return builder('child1', 'child2');\n  }\n\n  /// Even better... do it inline! (a.k.a fluent)\n  ReactElement baz() {\n    return (Foo()\n      ..id = 'the_best_foo'\n      ..color = 'red'\n    )(\n      'child1',\n      'child2'\n    );\n  }\n}\n```\n\n\u003e See [_fluent-style component consumption_](#fluent-style-component-consumption) for more examples on builder usage.\n\n\u0026nbsp;\n\n### UiState\n\n__`UiState` is a `Map` class__ _(just like `UiProps`)_ that adds statically-typed getters and setters\nfor each React component state property in a class component.\n\n```dart\nmixin FooState on UiState {\n  // ...\n}\n```\n\n\u003e `UiState` is optional, and won’t be used for every component. Check out the [`UiStatefulComponent` boilerplate](#stateful-component-boilerplate) for more information.\n\n* **Note:** The [builder] will make the concrete getters and setters available from the mixin fields you author in a generated class. To mix state classes together, the mixin class should be used rather than the generated props class. See [_\"With other mixins\"_](#with-other-mixins) above for more information.\n\n\u0026nbsp;\n\n### UiComponent2 (class-based components)\n\u003e For guidance on updating to `UiComponent2` from `UiComponent`, check out the [UiComponent2 Migration Guide](doc/ui_component2_transition.md).\n\n__`UiComponent2` is a subclass of [`react.Component2`][react.Component2]__, containing lifecycle methods and rendering logic for components.\n\n```dart\nclass FooComponent extends UiComponent2\u003cFooProps\u003e {\n  // ...\n}\n```\n\n* This class provides statically-typed props via [`UiProps`](#uiprops), as well as utilities for prop forwarding and CSS class merging.\n* The `UiStatefulComponent2` flavor augments `UiComponent2` behavior with statically-typed state via [`UiState`](#uistate).\n\n\u0026nbsp;\n\n### uiFunction (function components)\n__`uiFunction` lets you declare a function component__. \n\nIn JavaScript, function components are just plain functions, but in over_react this wrapper is needed to perform JS interop and wire up the typed props class.\n\n```dart\nmixin FooProps on UiProps {\n  bool? isDisabled;\n  List? items;\n}\n\nUiFactory\u003cFooProps\u003e Foo = uiFunction((props) {\n  // Set default props using null-aware operators.\n  final isDisabled = props.isDisabled ?? false;\n  final items = props.items ?? [];\n\n  // Return the rendered component contents here.\n  return Fragment()(\n    Dom.div()(items),\n    (Dom.button()..disabled = isDisabled)('Click me!'),\n  );\n}, _$FooConfig); // The generated props config will match the factory name.\n\nusageExample() =\u003e (Foo()..items = ['bar'])();\n```\n\n\u0026nbsp;\n\n#### Accessing and manipulating props / state within UiComponent2\n\n* Within the `UiComponent2` class, `props` and `state` are not just `Map`s.\nThey are instances of `UiProps` and `UiState`, __which means you don’t need String keys to access them!__\n* `newProps()` and `newState()` are also exposed to conveniently create empty instances of `UiProps` and `UiState` as needed.\n* `typedPropsFactory()` and `typedStateFactory()` are also exposed to conveniently create typed `props` / `state` objects out of any provided backing map.\n\n```dart\nUiFactory\u003cFooProps\u003e Foo = castUiFactory(_$Foo); // ignore: undefined_identifier\n\nmixin FooProps on UiProps {\n  late String color;\n  Function()? onDidActivate;\n  Function()? onDidDeactivate;\n}\nmixin FooState on UiState {\n  late bool isActive;\n}\n\nclass FooComponent extends UiStatefulComponent2\u003cFooProps, FooState\u003e {\n  @override\n  Map get defaultProps =\u003e (newProps()\n    ..color = '#66cc00'\n  );\n\n  @override\n  Map get initialState =\u003e (newState()\n    ..isActive = false\n  );\n\n  @override\n  void componentDidUpdate(Map prevProps, Map prevState, [dynamic snapshot]) {\n    var tPrevState = typedStateFactory(prevState);\n    var tPrevProps = typedPropsFactory(prevProps);\n\n    if (state.isActive \u0026\u0026 !tPrevState.isActive) {\n      props.onDidActivate?.call();\n    } else if (!state.isActive \u0026\u0026 tPrevState.isActive) {\n      props.onDidDeactivate?.call();\n    }\n  }\n\n  @override\n  dynamic render() {\n    return (Dom.div()\n      ..modifyProps(addUnconsumedDomProps)\n      ..style = {\n        ...newStyleFromProps(props),\n        'color': props.color,\n        'fontWeight': state.isActive ? 'bold' : 'normal', \n      }\n    )(\n      (Dom.button()..onClick = _handleButtonClick)('Toggle'),\n      props.children,\n    );\n  }\n\n  void _handleButtonClick(SyntheticMouseEvent event) {\n    setState(newState()\n      ..isActive = !state.isActive\n    );\n  }\n}\n```\n\n\u0026nbsp;\n\u0026nbsp;\n\n\n\n## Fluent-style component consumption\n\n\u003e The **[OverReact analyzer plugin](tools/analyzer_plugin/)** has many lints and assists to make authoring OverReact components easier!\n\nIn OverReact, components are consumed by invoking a `UiFactory` to return a new `UiProps` builder, which is then modified and invoked to build a [`ReactElement`][ReactElement].\n\nThis is done to make \"fluent-style\" component consumption possible, so that the OverReact consumer experience is very similar to the [React JS][react-js] / \"vanilla\" [react-dart]\nexperience.\n\nTo demonstrate the similarities, the example below shows a render method for JS, JSX, react-dart, and over_react that will have the exact same HTML markup result.\n\n* __React JS__:\n\n  ```es6\n  render() {\n    return React.createElement('div', {className: 'container'},\n      React.createElement('h1', null, 'Click the button!'),\n      React.createElement('button', {\n        id: 'main_button',\n        onClick: _handleClick\n      }, 'Click me')\n    );\n  }\n  ```\n\n* __React JS__ (JSX):\n\n  ```jsx\n  render() {\n    return \u003cdiv className=\"container\"\u003e\n      \u003ch1\u003eClick the button!\u003c/h1\u003e\n      \u003cbutton\n        id=\"main_button\"\n        onClick={_handleClick}\n      \u003eClick me\u003c/button\u003e\n    \u003c/div\u003e;\n  }\n  ```\n\n* __Vanilla react-dart__:\n\n  ```dart\n  render() {\n    return react.div({'className': 'container'},\n      react.h1({}, 'Click the button!'),\n      react.button({\n        'id': 'main_button',\n        'onClick': _handleClick\n      }, 'Click me')\n    );\n  }\n  ```\n\n* __OverReact__:\n\n  ```dart\n  render() {\n    return (Dom.div()..className = 'container')(\n      Dom.h1()('Click the button!'),\n      (Dom.button()\n        ..id = 'main_button'\n        ..onClick = _handleClick\n      )('Click me')\n    );\n  }\n  ```\n\n  Let’s break down the OverReact fluent-style shown above\n\n  ```dart\n  render() {\n    // Create a builder for a \u003cdiv\u003e,\n    // add a CSS class name by cascading a typed setter,\n    // and invoke the builder with the HTML DOM \u003ch1\u003e and \u003cbutton\u003e children.\n    return (Dom.div()..className = 'container')(\n\n      // Create a builder for an \u003ch1\u003e and invoke it with children.\n      // No need for wrapping parentheses, since no props are added.\n      Dom.h1()('Click the button!'),\n\n      // Create a builder for a \u003cbutton\u003e,\n      (Dom.button()\n        // add a ubiquitous DOM prop exposed on all components,\n        // which Dom.button() forwards to its rendered DOM,\n        ..id = 'main_button'\n        // add another prop,\n        ..onClick = _handleClick\n      // and finally invoke the builder with children.\n      )('Click me')\n    );\n  }\n  ```\n\n\u0026nbsp;\n\u0026nbsp;\n\n\n\n## DOM components and props\n\nAll [react-dart][react-dart] DOM components _(`react.div`, `react.a`, etc.)_ have a\ncorresponding `Dom` method _(`Dom.div()`, `Dom.a()`, etc.)_ in OverReact.\n\n```dart\nReactElement renderLink() {\n  return (Dom.a()\n    ..id = 'home_link'\n    ..href = '/home'\n  )('Home');\n}\n\nReactElement renderResizeHandle() {\n  return (Dom.div()\n    ..className = 'resize-handle'\n    ..onMouseDown = _startDrag\n  )();\n}\n```\n\n* OverReact DOM components return a new `DomProps` builder, which can be used\nto render them via our [fluent interface](#fluent-style-component-consumption)\nas shown in the examples above.\n  * `DomProps` has statically-typed getters and setters for all HTML attribute props.\n  * The `domProps()` function is also available to create a new typed Map or a typed view into an existing Map. Useful for manipulating DOM props and adding DOM props to components that don’t forward them directly, or to access a DOM prop from a plain map in a lifecycle method as shown below.\n  \n    ```dart\n    @override\n    void componentDidUpdate(Map prevProps, Map prevState, [dynamic snapshot]) {\n      // Say you want to compare the previous / current value of `DomProps.title` here...\n      final titleChanged = domProps(prevProps).title != props.title;\n    }\n    ```\n\n\u0026nbsp;\n\u0026nbsp;\n\n\n\n## Component Formatting\n\u003e __A note on dart_style:__\n\u003e\n\u003e Currently, [dart_style (dartfmt)](https://github.com/dart-lang/dart_style) decreases the readability of components\n\u003e built using [OverReact's fluent-style](#fluent-style-component-consumption).\n\u003e See https://github.com/dart-lang/dart_style/issues/549 for more info.\n\u003e\n\u003e We're exploring some different ideas to improve automated formatting, but for the time being, we __do not recommend__ using dart_style with OverReact.\n\u003e\n\u003e However, if you do choose to use dart_style, you can greatly improve its output by using trailing commas in children argument lists:\n\u003e\n\u003e * dart_style formatting:\n\u003e ```dart\n\u003e return (Button()\n\u003e   ..id = 'flip'\n\u003e   ..skin =\n\u003e       ButtonSkin.vanilla)((Dom.span()\n\u003e   ..className = 'flip-container')((Dom.span()..className = 'flipper')(\n\u003e     (Dom.span()\n\u003e       ..className =\n\u003e           'front-side')((Icon()..glyph = IconGlyph.CHEVRON_DOUBLE_RIGHT)()),\n\u003e     (Dom.span()\n\u003e       ..className =\n\u003e           'back-side')((Icon()..glyph = IconGlyph.CHEVRON_DOUBLE_LEFT)()))));\n\u003e ```\n\u003e * dart_style formatting, when trailing commas are used:\n\u003e ```dart\n\u003e return (Button()\n\u003e   ..id = 'flip'\n\u003e   ..skin = ButtonSkin.vanilla)(\n\u003e   (Dom.span()..className = 'flip-container')(\n\u003e     (Dom.span()..className = 'flipper')(\n\u003e       (Dom.span()..className = 'front-side')(\n\u003e         (Icon()..glyph = IconGlyph.CHEVRON_DOUBLE_RIGHT)(),\n\u003e       ),\n\u003e       (Dom.span()..className = 'back-side')(\n\u003e         (Icon()..glyph = IconGlyph.CHEVRON_DOUBLE_LEFT)(),\n\u003e       ),\n\u003e     ),\n\u003e   ),\n\u003e );\n\u003e ```\n\n### Guidelines\n\nTo help ensure your OverReact code is readable and consistent, we've arrived at the following formatting rules.\n\n* __ALWAYS__ place the closing builder parent on a new line.\n\n  _Good:_\n    ```dart\n    (Button()\n      ..skin = ButtonSkin.SUCCESS\n      ..isDisabled = true\n    )('Submit')\n    ```\n\n  _Bad:_\n    ```dart\n    (Button()\n      ..skin = ButtonSkin.SUCCESS\n      ..isDisabled = true)('Submit')\n    ```\n\n* __ALWAYS__ pass component children on a new line with trailing commas and 2 space indentation.\n\n  _Good:_\n    ```dart\n    Dom.div()(\n      Dom.span()('nested component'),\n    )\n    ```\n\n    ```dart\n    Dom.div()(\n      Dom.span()('nested component A'),\n      Dom.span()('nested component B'),\n    )\n    ```\n\n  _Bad:_\n    ```dart\n    // Children are not on a new line; in most cases,\n    // this makes it difficult to quickly determine nesting.\n    Dom.div()(Dom.span()('nested component'), Dom.span()('nested component'))\n    ```\n\n    ```dart\n    // With nested hierarchies, continuation indents can quickly result\n    // in a \"pyramid of Doom\"\n    Dom.div()(\n        Dom.ul()(\n            Dom.li()(\n                Dom.a()('A link!')\n            )\n        )\n    )\n    ```\n\n    ```dart\n    // Omitting trailing commas makes it a pain to rearrange lines\n    Dom.div()(\n      Dom.span()('nested component A'),\n      Dom.span()('nested component B')\n    )\n    Dom.div()(\n      Dom.span()('nested component B') // ugh, need to add a comma here...\n      Dom.span()('nested component A'),\n    )\n    ```\n\n* __AVOID__ passing children within lists; lists should only be used when the number/order of the children are dynamic.\n\n  _Good:_\n    ```dart\n    Dom.div()(\n      Dom.span()('nested component'),\n      Dom.span()('nested component'),\n    )\n    ```\n\n    ```dart\n    var children = [\n      Dom.div()('List of Items:'),\n    ]..addAll(props.items.map(renderItem));\n\n    return Dom.div()(children)\n    ```\n\n  _Bad:_\n    ```dart\n    Dom.div()([\n      (Dom.span()..key = 'span1')('nested component'),\n      (Dom.span()..key = 'span2')('nested component'),\n    ])\n    ```\n\n* __AVOID__ specifying more than one cascading prop setter on the same line.\n\n  _Good:_\n    ```dart\n    (Dom.div()\n      ..id = 'my_div'\n      ..className = 'my-class'\n    )()\n    ```\n\n  _Bad:_\n    ```dart\n    (Dom.div()..id = 'my_div'..className = 'my-class')()\n    ```\n\n\n## Building custom components\n\nNow that we’ve gone over how to [use the `over_react` package in your project](#using-overreact),\nthe [anatomy of a component](#anatomy-of-an-overreact-component) and the [DOM components](#dom-components-and-props)\nthat you get for free from OverReact, you're ready to start building your own custom React UI components.\n\n1. Start with one of the [component boilerplate templates](#component-boilerplate-templates) below\n(Or, use OverReact's [code snippets for WebStorm/IntelliJ and VsCode](snippets/README.md)).\n  * [Component](#component-boilerplate) _(props only)_\n  * [Stateful Component](#stateful-component-boilerplate) _(props + state)_\n2. Fill in your props and rendering/lifecycle logic.\n3. Consume your component with the fluent interface.\n4. Run [the app you’ve set up to consume `over_react`](#using-overreact)\n\n    ```bash\n    $ webdev serve\n    ```\n\n    _That’s it! Code will be automatically generated on the fly by the [builder]!_\n\n\n\u003e __Check out some custom [component demos] to get a feel for what’s possible!__\n\n\u0026nbsp;\n\n### Component Boilerplate Templates\n\n* #### [WebStorm/IntelliJ and VsCode Snippets](snippets/README.md)\n\n* #### Component Boilerplate\n\n    ```dart\n    import 'package:over_react/over_react.dart';\n    part 'foo_component.over_react.g.dart';\n\n    UiFactory\u003cFooProps\u003e Foo = castUiFactory(_$Foo); // ignore: undefined_identifier\n\n    mixin FooProps on UiProps {\n      // Props go here, declared as fields:\n      late bool isDisabled;\n      late Iterable\u003cString\u003e items;\n    }\n\n    class FooComponent extends UiComponent2\u003cFooProps\u003e {\n      @override\n      Map get defaultProps =\u003e (newProps()\n        // Cascade default props here\n        ..isDisabled = false\n        ..items = []\n      );\n\n      @override\n      dynamic render() {\n        // Return the rendered component contents here.\n        // The `props` variable is typed; no need for string keys!\n      }\n    }\n    ```\n\n* #### Stateful Component Boilerplate\n\n    ```dart\n    import 'package:over_react/over_react.dart';\n    part 'foo_component.over_react.g.dart';\n\n    UiFactory\u003cBarProps\u003e Bar = castUiFactory(_$Bar); // ignore: undefined_identifier\n\n    mixin BarProps on UiProps {\n      // Props go here, declared as fields:\n      late bool isDisabled;\n      late Iterable\u003cString\u003e items;\n    }\n\n    mixin BarState on UiState {\n      // State goes here, declared as fields:\n      late bool isShown;\n    }\n\n    class BarComponent extends UiStatefulComponent2\u003cBarProps, BarState\u003e {\n      @override\n      Map get defaultProps =\u003e (newProps()\n        // Cascade default props here\n        ..isDisabled = false\n        ..items = []\n      );\n\n      @override\n      Map get initialState =\u003e (newState()\n        // Cascade initial state here\n        ..isShown = true\n      );\n\n      @override\n      dynamic render() {\n        // Return the rendered component contents here.\n        // The `props` variable is typed; no need for string keys!\n      }\n    }\n    ```\n\n* #### Function Component Boilerplate\n\n  ```dart\n  import 'package:over_react/over_react.dart';\n  part 'foo_component.over_react.g.dart';\n\n  UiFactory\u003cFooProps\u003e Foo = uiFunction(\n    (props) {\n      // Set default props using null-aware operators.\n      final isDisabled = props.isDisabled ?? false;\n      final items = props.items ?? [];\n  \n      // Return the rendered component contents here.\n      // The `props` variable is typed; no need for string keys!\n      return Fragment()(\n        Dom.div()(items),\n        (Dom.button()..disabled = isDisabled)('Click me!'),\n      );\n    },\n    // The generated props config will match the factory name.\n    _$FooConfig, // ignore: undefined_identifier\n  );\n\n  mixin FooProps on UiProps {\n    // Props go here, declared as fields:\n    bool? isDisabled;\n    Iterable\u003cString\u003e? items;\n  }\n  ```\n\n\u0026nbsp;\n\n### Component Best Practices\n\n\n* __ALWAYS__ write informative comments for your component factories.\nInclude what the component relates to, relies on, or if it extends\nanother component.\n\n  _Good:_\n    ```dart\n    /// Use the `DropdownButton` component to render a button\n    /// that controls the visibility of a child [DropdownMenu].\n    ///\n    /// * Related to [Button].\n    /// * Extends [DropdownTrigger].\n    /// * Similar to [SplitButton].\n    ///\n    /// See: \u003chttps://link-to-any-relevant-documentation\u003e.\n    UiFactory\u003cDropdownButtonProps\u003e DropdownButton = castUiFactory(_$DropdownButton); // ignore: undefined_identifier\n    ```\n\n  _Bad:_\n    ```dart\n    /// Component Factory for a dropdown button component.\n    UiFactory\u003cDropdownButtonProps\u003e DropdownButton = castUiFactory(_$DropdownButton); // ignore: undefined_identifier\n    ```\n\n\u0026nbsp;\n\n* __ALWAYS__ set a default / initial value for boolean `props` / `state` fields,\nand document that value in a comment.\n\n  _Why?_ Without default prop values for bool fields, they could be\n  `null` - which is extremely confusing and can lead to a lot of\n  unnecessary null-checking in your business logic.\n\n  _Good:_\n    ```dart\n    mixin DropdownButtonProps on UiProps {\n      /// Whether the [DropdownButton] appears disabled.\n      ///\n      /// Default: `false`\n      bool isDisabled;\n\n      /// Whether the [DropdownButton]'s child [DropdownMenu] is open\n      /// when the component is first mounted.\n      ///\n      /// Determines the initial value of [DropdownButtonState.isOpen].\n      ///\n      /// Default: `false`\n      bool initiallyOpen;\n    }\n\n    mixin DropdownButtonState on UiState {\n      /// Whether the [DropdownButton]'s child [DropdownMenu] is open.\n      ///\n      /// Initial: [DropdownButtonProps.initiallyOpen]\n      bool isOpen;\n    }\n\n    DropdownButtonComponent\n        extends UiStatefulComponent2\u003cDropdownButtonProps, DropdownButtonState\u003e {\n      @override\n      Map get defaultProps =\u003e (newProps()\n        ..isDisabled = false\n        ..initiallyOpen = false\n      );\n\n      @override\n      Map get initialState =\u003e (newState()\n        ..isOpen = props.initiallyOpen\n      );\n    }\n    ```\n\n  _Bad:_\n    ```dart\n    mixin DropdownButtonProps on UiProps {\n      bool isDisabled;\n      bool initiallyOpen;\n    }\n\n    mixin DropdownButtonState on UiState {\n      bool isOpen;\n    }\n\n    DropdownButtonComponent\n        extends UiStatefulComponent2\u003cDropdownButtonProps, DropdownButtonState\u003e {\n      // Confusing stuff is gonna happen in here with\n      // bool props that could be null.\n    }\n    ```\n\n\u0026nbsp;\n\n* __AVOID__ adding `props` or `state` fields that don't have\nan informative comment.\n\n  _Good:_\n    ```dart\n    mixin DropdownButtonProps on UiProps {\n      /// Whether the [DropdownButton] appears disabled.\n      ///\n      /// Default: `false`\n      bool isDisabled;\n\n      /// Whether the [DropdownButton]'s child [DropdownMenu] is open\n      /// when the component is first mounted.\n      ///\n      /// Determines the initial value of [DropdownButtonState.isOpen].\n      ///\n      /// Default: `false`\n      bool initiallyOpen;\n    }\n\n    mixin DropdownButtonState on UiState {\n      /// Whether the [DropdownButton]'s child [DropdownMenu] is open.\n      ///\n      /// Initial: [DropdownButtonProps.initiallyOpen]\n      bool isOpen;\n    }\n    ```\n\n  _Bad:_\n    ```dart\n    mixin DropdownButtonProps on UiProps {\n      bool isDisabled;\n      bool initiallyOpen;\n    }\n\n    mixin DropdownButtonState on UiState {\n      bool isOpen;\n    }\n    ```\n\n\u0026nbsp;\n\n#### Ignore Ungenerated Warnings Project-Wide\n\nTo avoid having to add `// ignore: uri_has_not_been_generated` to each\ncomponent library on the part/import that references generated code,\nignore this warning globally within analysis_options.yaml:\n\n```yaml\n analyzer:\n   errors:\n     uri_has_not_been_generated: ignore\n```\n\nAlternatively, `include` [workiva_analysis_options](https://github.com/Workiva/workiva_analysis_options)\nwhich ignores this warning by default.\n\n\u0026nbsp;\n\n## Contributing\n\nYes please! ([__Please read our contributor guidelines first__][contributing-docs])\n\n\u0026nbsp;\n\u0026nbsp;\n\n\n\n## Versioning\n\nThe `over_react` library adheres to [Semantic Versioning](https://semver.org/):\n\n* Any API changes that are not backwards compatible will __bump the major version__ _(and reset the minor / patch)_.\n* Any new functionality that is added in a backwards-compatible manner will __bump the minor version__\n  _(and reset the patch)_.\n* Any backwards-compatible bug fixes that are added will __bump the patch version__.\n\n\n\n[component demos]: https://workiva.github.io/over_react/demos\n\n[contributing-docs]: https://github.com/Workiva/over_react/blob/master/CONTRIBUTING.md\n[builder]: https://github.com/Workiva/over_react/blob/master/lib/src/builder/README.md\n[annotations]: https://github.com/Workiva/over_react/blob/master/lib/src/component_declaration/annotations.dart\n[annotation]: https://github.com/Workiva/over_react/blob/master/lib/src/component_declaration/annotations.dart\n[component_base.dart]: https://github.com/Workiva/over_react/blob/master/lib/src/component_declaration/component_base.dart\n\n[react-dart]: https://github.com/cleandart/react-dart\n[react-js]: https://github.com/facebook/react\n[react-js-tutorial]: https://reactjs.org/docs/getting-started.html\n\n[react.Component2]: https://pub.dev/documentation/react/latest/react/Component2-class.html\n[ReactElement]: https://pub.dev/documentation/react/latest/react_client.react_interop/ReactElement-class.html\n\n[new-issue]: https://github.com/Workiva/over_react/issues/new\n[gitter-chat]: https://gitter.im/over_react/Lobby\n\n[analyzer-plugin]: tools/analyzer_plugin/\n","funding_links":[],"categories":["Client Web App Frameworks","Web和客户端"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/workiva.github.io%2Fover_react%2F","html_url":"https://awesome.ecosyste.ms/projects/workiva.github.io%2Fover_react%2F","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/workiva.github.io%2Fover_react%2F/lists"}