Code Monkey home page Code Monkey logo

over_react's Introduction

OverReact

A library for building statically-typed React UI components using Dart.

This library also exposes OverReact Redux, which has its own documentation.

Pub Documentation OverReact Analyzer Plugin (beta) Join the gitter chat

Dart CI


Migration Guides

Null safety

over_react 5.0.0 introduces support for null safety.

Now, you can declare non-nullable required props, using the late keyword. See the docs null safety and required props for more information.

To migrate components to null safety, see our null safety migration guide.

More Migration Guides

  • New component boilerplate: How to update to the mixin-based (mixin MyProps on UiProps {}) over_react component declaration syntax.
  • UiComponent2: How to move off the deprecated UiComponent base class and onto UiComponent2.
  • BuiltRedux to Redux: How to transition to OverReact Redux from BuiltRedux.
  • Flux to Redux: 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.
  • Dart2 Migration: Documentation on the Dart 2 builder updates and how to transition componentry to Dart 2.

   

Using OverReact

Prerequisites

  • Familiarize yourself with React JS

    Since OverReact is built atop React JS, we strongly encourage you to gain familiarity with it by reading some React JS tutorials first.

  • Familiarize yourself with Dart Web applications

    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.

  1. Add the over_react package as a dependency in your pubspec.yaml.

    dependencies:
      over_react: ^4.0.0
  2. Enable the OverReact Analyzer Plugin (beta), which has many lints and assists to make authoring OverReact components easier!

  3. Include the native JavaScript react and react_dom libraries in your app’s index.html file, and add an HTML element with a unique identifier where you’ll mount your OverReact UI component(s).

    <html>
      <head>
        <!-- ... -->
      </head>
      <body>
        <div id="react_mount_point">
          // OverReact component render() output will show up here.
        </div>
    
        <script src="packages/react/react.js"></script>
        <script src="packages/react/react_dom.js"></script>
    
        <!-- NOTE: "index" should correspond to the
             name of the `.dart` file that contains your `main()` entrypoint. -->
        <script type="application/javascript" defer src="index.dart.js"></script>
      </body>
    </html>

    Note: When serving your application in production, use packages/react/react_with_react_dom_prod.js file instead of the un-minified react.js / react_dom.js files shown in the example above.

  4. Import the over_react and react_dom libraries into index.dart. Then build some 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.

    Be sure to namespace the react_dom.dart import as react_dom to avoid collisions with UiComponent.render when creating custom components.

    import 'dart:html';
    import 'package:over_react/react_dom.dart' as react_dom;
    import 'package:over_react/over_react.dart';
    
    // Example of where the `Foo` component might be exported from
    import 'package:your_package_name/foo.dart';
    
    main() {
      // Mount / render your component/application.
      react_dom.render(
        Foo()(),
        querySelector('#react_mount_point'),
      );
    }
  5. Run webdev serve in the root of your Dart project.

Note: If you're not using the latest component boilerplate, 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.

Migrate your components to the latest component boilerplate to never worry about this again!

 

Running unit tests in your project

When running unit tests on code that uses the over_react builder (or any code that imports over_react), you must run your tests using the build_runner package.

Warning: Do not run tests via pub run build_runner test in a package while another instance of build_runner (e.g. pub run build_runner serve) is running in that same package. This workflow is unsupported by build_runner

  1. Run tests using the build_runner package, and specify the platform to be a browser platform. Example:

    $ pub run build_runner test -- -p chrome test/your_test_file.dart

OverReact Component Unit Test Examples

Below are links to a UI component from our example "Todo App", and its analogous tests that we've written for components we use in . We utilize the utilities found in our over_react_test library.

   

Anatomy of an OverReact component

If you are not familiar with React JS

Since OverReact is built atop React JS, we strongly encourage you to gain familiarity with it by reading this React JS tutorial first.

The over_react library functions as an additional "layer" atop the Dart react package which handles the underlying JS interop that wraps around React JS.

The library strives to maintain a 1:1 relationship with the React JS component class and API. To do that, an OverReact component is comprised of four core pieces that are each wired up via our builder.

  1. UiFactory
  2. UiProps
  3. component, either a:
    1. function component uiFunction
    2. class component UiComponent2 (and optionally a UiState)

 

UiFactory

UiFactory is a function that returns a new instance of a component's UiProps class.

// Class component
UiFactory<FooProps> Foo = castUiFactory(_$Foo);

// Function component
UiFactory<FooProps> Foo = uiFunction((props) { /*...*/ }, _$FooConfig);

 

UiProps

UiProps is a Map class that adds statically-typed getters and setters for each React component prop. It can also be invoked as a function, serving as a builder for its analogous component.

mixin FooProps on UiProps {
  // ... the props for your component go here
  String? bar;
  bool? baz;
  List<int>? bizzles;
}
    • 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" below for more information.

 

With other mixins

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.

UiFactory<FooProps> Foo = castUiFactory(_$Foo); // ignore: undefined_identifier

mixin FooPropsMixin on UiProps {
  String? bar;
  bool? baz;
  List<int>? bizzles;
}

class FooProps = UiProps with FooPropsMixin, BarPropsMixin;

class FooComponent extends UiComponent2<FooProps> {
  // ...
}
Composition

The 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.

Check out an example of props mixin component composition here

 

UiProps as a Map

Warning

Directly reading late required props on arbitrary maps is unsafe.

See the docs on Unsafe Required Prop Reads for more information and for instructions on how to read these props safely.

UiFactory<FooProps> Foo = castUiFactory(_$Foo); // ignore: undefined_identifier

mixin FooProps on UiProps {
  String? color;
}

class FooComponent extends UiComponent2<FooProps> {
  // ...
}

void bar() {
  FooProps props = Foo();

  props.color = '#66cc00';

  print(props.color); // #66cc00
  print(props);       // {FooProps.color: #66cc00}
}

/// You can also use the factory to create a UiProps instance
/// backed by an existing Map.
void baz() {
  Map existingMap = {'FooProps.color': '#0094ff'};

  FooProps props = Foo(existingMap);

  print(props.color); // #0094ff
}

 

UiProps as a builder

UiFactory<FooProps> Foo = castUiFactory(_$Foo); // ignore: undefined_identifier

mixin FooProps on UiProps {
  String? color;
}

class FooComponent extends UiComponent2<FooProps> {
  ReactElement bar() {
    // Create a UiProps instance to serve as a builder
    FooProps builder = Foo();

    // Set some prop values
    builder
      ..id = 'the_best_foo'
      ..color = '#ee2724';

    // Invoke as a function with the desired children
    // to return a new instance of the component.
    return builder('child1', 'child2');
  }

  /// Even better... do it inline! (a.k.a fluent)
  ReactElement baz() {
    return (Foo()
      ..id = 'the_best_foo'
      ..color = 'red'
    )(
      'child1',
      'child2'
    );
  }
}

See fluent-style component consumption for more examples on builder usage.

 

UiState

UiState is a Map class (just like UiProps) that adds statically-typed getters and setters for each React component state property in a class component.

mixin FooState on UiState {
  // ...
}

UiState is optional, and won’t be used for every component. Check out the UiStatefulComponent boilerplate for more information.

  • 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" above for more information.

 

UiComponent2 (class-based components)

For guidance on updating to UiComponent2 from UiComponent, check out the UiComponent2 Migration Guide.

UiComponent2 is a subclass of react.Component2, containing lifecycle methods and rendering logic for components.

class FooComponent extends UiComponent2<FooProps> {
  // ...
}
  • This class provides statically-typed props via UiProps, as well as utilities for prop forwarding and CSS class merging.
  • The UiStatefulComponent2 flavor augments UiComponent2 behavior with statically-typed state via UiState.

 

uiFunction (function components)

uiFunction lets you declare a function component.

In 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.

mixin FooProps on UiProps {
  bool? isDisabled;
  List? items;
}

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.
  return Fragment()(
    Dom.div()(items),
    (Dom.button()..disabled = isDisabled)('Click me!'),
  );
}, _$FooConfig); // The generated props config will match the factory name.

usageExample() => (Foo()..items = ['bar'])();

 

Accessing and manipulating props / state within UiComponent2

  • Within the UiComponent2 class, props and state are not just Maps. They are instances of UiProps and UiState, which means you don’t need String keys to access them!
  • newProps() and newState() are also exposed to conveniently create empty instances of UiProps and UiState as needed.
  • typedPropsFactory() and typedStateFactory() are also exposed to conveniently create typed props / state objects out of any provided backing map.
UiFactory<FooProps> Foo = castUiFactory(_$Foo); // ignore: undefined_identifier

mixin FooProps on UiProps {
  late String color;
  Function()? onDidActivate;
  Function()? onDidDeactivate;
}
mixin FooState on UiState {
  late bool isActive;
}

class FooComponent extends UiStatefulComponent2<FooProps, FooState> {
  @override
  Map get defaultProps => (newProps()
    ..color = '#66cc00'
  );

  @override
  Map get initialState => (newState()
    ..isActive = false
  );

  @override
  void componentDidUpdate(Map prevProps, Map prevState, [dynamic snapshot]) {
    var tPrevState = typedStateFactory(prevState);
    var tPrevProps = typedPropsFactory(prevProps);

    if (state.isActive && !tPrevState.isActive) {
      props.onDidActivate?.call();
    } else if (!state.isActive && tPrevState.isActive) {
      props.onDidDeactivate?.call();
    }
  }

  @override
  dynamic render() {
    return (Dom.div()
      ..modifyProps(addUnconsumedDomProps)
      ..style = {
        ...newStyleFromProps(props),
        'color': props.color,
        'fontWeight': state.isActive ? 'bold' : 'normal', 
      }
    )(
      (Dom.button()..onClick = _handleButtonClick)('Toggle'),
      props.children,
    );
  }

  void _handleButtonClick(SyntheticMouseEvent event) {
    setState(newState()
      ..isActive = !state.isActive
    );
  }
}

   

Fluent-style component consumption

The OverReact analyzer plugin has many lints and assists to make authoring OverReact components easier!

In OverReact, components are consumed by invoking a UiFactory to return a new UiProps builder, which is then modified and invoked to build a ReactElement.

This is done to make "fluent-style" component consumption possible, so that the OverReact consumer experience is very similar to the React JS / "vanilla" react-dart experience.

To 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.

  • React JS:

    render() {
      return React.createElement('div', {className: 'container'},
        React.createElement('h1', null, 'Click the button!'),
        React.createElement('button', {
          id: 'main_button',
          onClick: _handleClick
        }, 'Click me')
      );
    }
  • React JS (JSX):

    render() {
      return <div className="container">
        <h1>Click the button!</h1>
        <button
          id="main_button"
          onClick={_handleClick}
        >Click me</button>
      </div>;
    }
  • Vanilla react-dart:

    render() {
      return react.div({'className': 'container'},
        react.h1({}, 'Click the button!'),
        react.button({
          'id': 'main_button',
          'onClick': _handleClick
        }, 'Click me')
      );
    }
  • OverReact:

    render() {
      return (Dom.div()..className = 'container')(
        Dom.h1()('Click the button!'),
        (Dom.button()
          ..id = 'main_button'
          ..onClick = _handleClick
        )('Click me')
      );
    }

    Let’s break down the OverReact fluent-style shown above

    render() {
      // Create a builder for a <div>,
      // add a CSS class name by cascading a typed setter,
      // and invoke the builder with the HTML DOM <h1> and <button> children.
      return (Dom.div()..className = 'container')(
    
        // Create a builder for an <h1> and invoke it with children.
        // No need for wrapping parentheses, since no props are added.
        Dom.h1()('Click the button!'),
    
        // Create a builder for a <button>,
        (Dom.button()
          // add a ubiquitous DOM prop exposed on all components,
          // which Dom.button() forwards to its rendered DOM,
          ..id = 'main_button'
          // add another prop,
          ..onClick = _handleClick
        // and finally invoke the builder with children.
        )('Click me')
      );
    }

   

DOM components and props

All react-dart DOM components (react.div, react.a, etc.) have a corresponding Dom method (Dom.div(), Dom.a(), etc.) in OverReact.

ReactElement renderLink() {
  return (Dom.a()
    ..id = 'home_link'
    ..href = '/home'
  )('Home');
}

ReactElement renderResizeHandle() {
  return (Dom.div()
    ..className = 'resize-handle'
    ..onMouseDown = _startDrag
  )();
}
  • OverReact DOM components return a new DomProps builder, which can be used to render them via our fluent interface as shown in the examples above.
    • DomProps has statically-typed getters and setters for all HTML attribute props.

    • 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.

      @override
      void componentDidUpdate(Map prevProps, Map prevState, [dynamic snapshot]) {
        // Say you want to compare the previous / current value of `DomProps.title` here...
        final titleChanged = domProps(prevProps).title != props.title;
      }

   

Component Formatting

A note on dart_style:

Currently, dart_style (dartfmt) decreases the readability of components built using OverReact's fluent-style. See dart-lang/dart_style#549 for more info.

We're exploring some different ideas to improve automated formatting, but for the time being, we do not recommend using dart_style with OverReact.

However, if you do choose to use dart_style, you can greatly improve its output by using trailing commas in children argument lists:

  • dart_style formatting:
return (Button()
  ..id = 'flip'
  ..skin =
      ButtonSkin.vanilla)((Dom.span()
  ..className = 'flip-container')((Dom.span()..className = 'flipper')(
    (Dom.span()
      ..className =
          'front-side')((Icon()..glyph = IconGlyph.CHEVRON_DOUBLE_RIGHT)()),
    (Dom.span()
      ..className =
          'back-side')((Icon()..glyph = IconGlyph.CHEVRON_DOUBLE_LEFT)()))));
  • dart_style formatting, when trailing commas are used:
return (Button()
  ..id = 'flip'
  ..skin = ButtonSkin.vanilla)(
  (Dom.span()..className = 'flip-container')(
    (Dom.span()..className = 'flipper')(
      (Dom.span()..className = 'front-side')(
        (Icon()..glyph = IconGlyph.CHEVRON_DOUBLE_RIGHT)(),
      ),
      (Dom.span()..className = 'back-side')(
        (Icon()..glyph = IconGlyph.CHEVRON_DOUBLE_LEFT)(),
      ),
    ),
  ),
);

Guidelines

To help ensure your OverReact code is readable and consistent, we've arrived at the following formatting rules.

  • ALWAYS place the closing builder parent on a new line.

    Good:

    (Button()
      ..skin = ButtonSkin.SUCCESS
      ..isDisabled = true
    )('Submit')

    Bad:

    (Button()
      ..skin = ButtonSkin.SUCCESS
      ..isDisabled = true)('Submit')
  • ALWAYS pass component children on a new line with trailing commas and 2 space indentation.

    Good:

    Dom.div()(
      Dom.span()('nested component'),
    )
    Dom.div()(
      Dom.span()('nested component A'),
      Dom.span()('nested component B'),
    )

    Bad:

    // Children are not on a new line; in most cases,
    // this makes it difficult to quickly determine nesting.
    Dom.div()(Dom.span()('nested component'), Dom.span()('nested component'))
    // With nested hierarchies, continuation indents can quickly result
    // in a "pyramid of Doom"
    Dom.div()(
        Dom.ul()(
            Dom.li()(
                Dom.a()('A link!')
            )
        )
    )
    // Omitting trailing commas makes it a pain to rearrange lines
    Dom.div()(
      Dom.span()('nested component A'),
      Dom.span()('nested component B')
    )
    Dom.div()(
      Dom.span()('nested component B') // ugh, need to add a comma here...
      Dom.span()('nested component A'),
    )
  • AVOID passing children within lists; lists should only be used when the number/order of the children are dynamic.

    Good:

    Dom.div()(
      Dom.span()('nested component'),
      Dom.span()('nested component'),
    )
    var children = [
      Dom.div()('List of Items:'),
    ]..addAll(props.items.map(renderItem));
    
    return Dom.div()(children)

    Bad:

    Dom.div()([
      (Dom.span()..key = 'span1')('nested component'),
      (Dom.span()..key = 'span2')('nested component'),
    ])
  • AVOID specifying more than one cascading prop setter on the same line.

    Good:

    (Dom.div()
      ..id = 'my_div'
      ..className = 'my-class'
    )()

    Bad:

    (Dom.div()..id = 'my_div'..className = 'my-class')()

Building custom components

Now that we’ve gone over how to use the over_react package in your project, the anatomy of a component and the DOM components that you get for free from OverReact, you're ready to start building your own custom React UI components.

  1. Start with one of the component boilerplate templates below (Or, use OverReact's code snippets for WebStorm/IntelliJ and VsCode).
  1. Fill in your props and rendering/lifecycle logic.

  2. Consume your component with the fluent interface.

  3. Run the app you’ve set up to consume over_react

    $ webdev serve

    That’s it! Code will be automatically generated on the fly by the builder!

Check out some custom component demos to get a feel for what’s possible!

 

Component Boilerplate Templates

  • Component Boilerplate

    import 'package:over_react/over_react.dart';
    part 'foo_component.over_react.g.dart';
    
    UiFactory<FooProps> Foo = castUiFactory(_$Foo); // ignore: undefined_identifier
    
    mixin FooProps on UiProps {
      // Props go here, declared as fields:
      late bool isDisabled;
      late Iterable<String> items;
    }
    
    class FooComponent extends UiComponent2<FooProps> {
      @override
      Map get defaultProps => (newProps()
        // Cascade default props here
        ..isDisabled = false
        ..items = []
      );
    
      @override
      dynamic render() {
        // Return the rendered component contents here.
        // The `props` variable is typed; no need for string keys!
      }
    }
  • Stateful Component Boilerplate

    import 'package:over_react/over_react.dart';
    part 'foo_component.over_react.g.dart';
    
    UiFactory<BarProps> Bar = castUiFactory(_$Bar); // ignore: undefined_identifier
    
    mixin BarProps on UiProps {
      // Props go here, declared as fields:
      late bool isDisabled;
      late Iterable<String> items;
    }
    
    mixin BarState on UiState {
      // State goes here, declared as fields:
      late bool isShown;
    }
    
    class BarComponent extends UiStatefulComponent2<BarProps, BarState> {
      @override
      Map get defaultProps => (newProps()
        // Cascade default props here
        ..isDisabled = false
        ..items = []
      );
    
      @override
      Map get initialState => (newState()
        // Cascade initial state here
        ..isShown = true
      );
    
      @override
      dynamic render() {
        // Return the rendered component contents here.
        // The `props` variable is typed; no need for string keys!
      }
    }
  • Function Component Boilerplate

    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;
    }

 

Component Best Practices

  • ALWAYS write informative comments for your component factories. Include what the component relates to, relies on, or if it extends another component.

    Good:

    /// Use the `DropdownButton` component to render a button
    /// that controls the visibility of a child [DropdownMenu].
    ///
    /// * Related to [Button].
    /// * Extends [DropdownTrigger].
    /// * Similar to [SplitButton].
    ///
    /// See: <https://link-to-any-relevant-documentation>.
    UiFactory<DropdownButtonProps> DropdownButton = castUiFactory(_$DropdownButton); // ignore: undefined_identifier

    Bad:

    /// Component Factory for a dropdown button component.
    UiFactory<DropdownButtonProps> DropdownButton = castUiFactory(_$DropdownButton); // ignore: undefined_identifier

 

  • ALWAYS set a default / initial value for boolean props / state fields, and document that value in a comment.

    Why? Without default prop values for bool fields, they could be null - which is extremely confusing and can lead to a lot of unnecessary null-checking in your business logic.

    Good:

    mixin DropdownButtonProps on UiProps {
      /// Whether the [DropdownButton] appears disabled.
      ///
      /// Default: `false`
      bool isDisabled;
    
      /// Whether the [DropdownButton]'s child [DropdownMenu] is open
      /// when the component is first mounted.
      ///
      /// Determines the initial value of [DropdownButtonState.isOpen].
      ///
      /// Default: `false`
      bool initiallyOpen;
    }
    
    mixin DropdownButtonState on UiState {
      /// Whether the [DropdownButton]'s child [DropdownMenu] is open.
      ///
      /// Initial: [DropdownButtonProps.initiallyOpen]
      bool isOpen;
    }
    
    DropdownButtonComponent
        extends UiStatefulComponent2<DropdownButtonProps, DropdownButtonState> {
      @override
      Map get defaultProps => (newProps()
        ..isDisabled = false
        ..initiallyOpen = false
      );
    
      @override
      Map get initialState => (newState()
        ..isOpen = props.initiallyOpen
      );
    }

    Bad:

    mixin DropdownButtonProps on UiProps {
      bool isDisabled;
      bool initiallyOpen;
    }
    
    mixin DropdownButtonState on UiState {
      bool isOpen;
    }
    
    DropdownButtonComponent
        extends UiStatefulComponent2<DropdownButtonProps, DropdownButtonState> {
      // Confusing stuff is gonna happen in here with
      // bool props that could be null.
    }

 

  • AVOID adding props or state fields that don't have an informative comment.

    Good:

    mixin DropdownButtonProps on UiProps {
      /// Whether the [DropdownButton] appears disabled.
      ///
      /// Default: `false`
      bool isDisabled;
    
      /// Whether the [DropdownButton]'s child [DropdownMenu] is open
      /// when the component is first mounted.
      ///
      /// Determines the initial value of [DropdownButtonState.isOpen].
      ///
      /// Default: `false`
      bool initiallyOpen;
    }
    
    mixin DropdownButtonState on UiState {
      /// Whether the [DropdownButton]'s child [DropdownMenu] is open.
      ///
      /// Initial: [DropdownButtonProps.initiallyOpen]
      bool isOpen;
    }

    Bad:

    mixin DropdownButtonProps on UiProps {
      bool isDisabled;
      bool initiallyOpen;
    }
    
    mixin DropdownButtonState on UiState {
      bool isOpen;
    }

 

Ignore Ungenerated Warnings Project-Wide

To avoid having to add // ignore: uri_has_not_been_generated to each component library on the part/import that references generated code, ignore this warning globally within analysis_options.yaml:

 analyzer:
   errors:
     uri_has_not_been_generated: ignore

Alternatively, include workiva_analysis_options which ignores this warning by default.

 

Contributing

Yes please! (Please read our contributor guidelines first)

   

Versioning

The over_react library adheres to Semantic Versioning:

  • Any API changes that are not backwards compatible will bump the major version (and reset the minor / patch).
  • Any new functionality that is added in a backwards-compatible manner will bump the minor version (and reset the patch).
  • Any backwards-compatible bug fixes that are added will bump the patch version.

over_react's People

Contributors

aaronlademann-wf avatar brandonrodgers-wf avatar brentarndorfer-wf avatar clairesarsam-wf avatar corwinsheahan-wf avatar dianachen-wf avatar evanweible-wf avatar greglittlefield-wf avatar hunterbreathat-wk avatar jacehensley-wf avatar joebingham-wk avatar joelleibow-wf avatar kaanaksoy-wk avatar kealjones-wk avatar leviwith-wf avatar luisgomez-wk avatar rm-astro-wf avatar rmconsole-readonly-wk avatar rmconsole2-wf avatar rmconsole3-wf avatar rmconsole4-wk avatar rmconsole5-wk avatar rmconsole6-wk avatar rmconsole7-wk avatar robbecker-wf avatar sebastianmalysa-wf avatar sydneesampson-wk avatar sydneyjodon-wk avatar tainhenning-wk avatar timothysteward-wk avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

over_react's Issues

textLength field missing for SVG element Dom.text()

  • Issue Type: [BUG]
  • over_react Version(s): 2.5.2

The SVG text element should have textLength as an available field (documented here: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/textLength), but it does not.

The following should work but fails to compile:

ReactElement text_elt = (Dom.text()..textLength='50')('hello');

The workaround is to assigned the property by a String key in the intermediate object of type SvgProps :

SvgProps text_props = Dom.text();
text_props.addProp('textLength', '50');
ReactElement text_elt = text_props('hello');

FYI: @greglittlefield-wf @aaronlademann-wf @kealjones-wk @evanweible-wf @maxwellpeterson-wf

UIP-3221 react.js and react_dom.js must be loaded. Error after follow Readme

  • Issue Type: [BUG?, DOCUMENTATION]
  • over_react Version(s): 1.23.0

I want to get started with over_react but following the instruction on the readme lead to an error I cannot solve as a Dart beginners.

Here the steps I follow

  1. Install Dart 2 with brew on Mac OS
  2. Install Intellij Dart and flutter plugin
  3. I create a basic web project in intellij (not angular)
  4. Follow the steps in the README (after step 2 I did pub get

This is the error I have :

react_interop.dart:32 Uncaught ReferenceError: render is not defined
    at StaticClosure.dart.ReactDom_render (react_interop.dart:32)
    at StaticClosure.dart.main (main.dart:56)
    at _IsolateContext.eval$1 (isolate_helper.dart:475)
    at Object.startRootIsolate (isolate_helper.dart:130)

I though it could be that I have to manual add the dependencies. But still no luck.

Here is a repo of the code I have after these steps https://github.com/francisl/test_over_react


FYI: @greglittlefield-wf @aaronlademann-wf @jacehensley-wf @clairesarsam-wf @joelleibow-wf

Dart 2 Builder

A builder with the same functionality that replaces the Dart 1 transformer.

UIP-2117 Run JS unit tests in Travis

Travis is slow enough that the loading of our JS unit test suites times out the test package. See dart-lang/test/issues/390

For now, we can run the tests on our private CI, but it would be great to have those results available publicly.

As an alternative solution, we could also run them on Sauce Labs.

Bridging Component Libraries

  • DOCUMENTATION_REQUEST
  • over_react Version(s): any

I'm trying to bridge an existing React component library (https://blueprintjs.com/) and it would be great if you can point me to docs on the cleanest way do that.

For a simple example like https://blueprintjs.com/docs/#core/components/text

@Factory()
UiFactory<TextProps> Text;

@Props()
class TextProps extends UiProps {
  String className;
  String ellipsize;
  String tagName;
}

@Component()
class TextComponent extends UiComponent<TextProps> {
  // Assuming the JS for the component library has already been loaded, what would go here? 
}

If you can fill in the example or let me know if there an bridged examples in the codebase already I can take it from there.


FYI: @greglittlefield-wf @aaronlademann-wf @jacehensley-wf @clairesarsam-wf @joelleibow-wf

UIP-1639 Update formatting guidelines WRT dartfmt and trailing commas

Our formatting guidelines currently recommend against using dartfmt/dart_style on over_react code.

We recently discovered that dartfmt is capable of producing significantly more readable over_react code, due to changes in dart_style that treat Flutter code better when nested components use trailing commas.

For more info, see dart-lang/dart_style#549. Thanks to @skybrian for getting the conversation started, and to @matanlurey for suggesting we revisit the issue!

We should upgrade the formatting guidelines to:

  • recommend trailing commas whenever supplying variadic children to a builder, even if it's just one
  • indicate that dartfmt output, while not perfect, is acceptable when trailing commas are used

FYI: @greglittlefield-wf @aaronlademann-wf @jacehensley-wf @clairesarsam-wf @joelleibow-wf

OverReactReduxDevToolsMiddleware

  • Issue Type: BUG
  • over_react Version(s): 3.5.0

When loading up my web app using the Redux DevTools extension, the console issues the following warning:

OverReactReduxDevToolsMiddleware [WARNING]: Assertion failed: org-dartlang-sdk:///sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/types.dart:87:10
_isJsObject(f) || !JS('bool', '# instanceof #.Function', f, global_)
"Dart function requires `allowInterop` to be passed to JavaScript."

The stack trace reads:

react_devtools_backend.js:6 OverReactReduxDevToolsMiddleware [WARNING]: Assertion failed: org-dartlang-sdk:///sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/types.dart:87:10
_isJsObject(f) || !JS('bool', '# instanceof #.Function', f, global_)
"Dart function requires `allowInterop` to be passed to JavaScript."
r @ react_devtools_backend.js:6
warn @ dart_sdk.js:97415
(anonymous) @ middleware.dart:47
runUnaryGuarded @ dart_sdk.js:31199
[_sendData] @ dart_sdk.js:25426
[_add] @ dart_sdk.js:25374
[_sendData] @ dart_sdk.js:26061
add @ dart_sdk.js:25867
[_publish] @ logger.dart:241
log @ logger.dart:187
warning @ logger.dart:220
middleware._OverReactReduxDevToolsMiddleware.new @ middleware.dart:56
get overReactReduxDevToolsMiddleware @ middleware.dart:187
desc.get @ dart_sdk.js:4908
initialize_model @ app.dart:109
runBody @ dart_sdk.js:31390
_async @ dart_sdk.js:31418
initialize_model @ app.dart:99
start @ app.dart:86
runBody @ dart_sdk.js:31390
_async @ dart_sdk.js:31418
start @ app.dart:80
main$ @ main.dart:4
(anonymous) @ VM733:1
runMain @ client.js:7952
(anonymous) @ client.js:23849
(anonymous) @ client.js:3003
call$2 @ client.js:10800
_asyncStartSync @ client.js:2968
$call$body$main__closure @ client.js:23861
call$1 @ client.js:23792
_rootRunUnary @ client.js:3303
runUnary$2$2 @ client.js:12130
runUnaryGuarded$1$2 @ client.js:12058
_sendData$1 @ client.js:11601
perform$1 @ client.js:11754
call$0 @ client.js:11807
_microtaskLoop @ client.js:3168
_startMicrotaskLoop @ client.js:3174
call$1 @ client.js:10675
invokeClosure @ client.js:1188
(anonymous) @ client.js:1207
childList (async)
call$1 @ client.js:10685
_scheduleAsyncCallback @ client.js:3187
_rootScheduleMicrotask @ client.js:3348
scheduleMicrotask @ client.js:3215
schedule$1 @ client.js:11789
_addPending$1 @ client.js:11592
_sendData$1 @ client.js:11411
add$1 @ client.js:11328
_onIncomingMessage$1 @ client.js:23399
eval @ VM718:3
invokeClosure @ client.js:1188
(anonymous) @ client.js:1207

It might have to do with the breaking change in Dart 2.8.0 described here, relating to allowInterop


FYI: @greglittlefield-wf @aaronlademann-wf @kealjones-wk @evanweible-wf @maxwellpeterson-wf

UIP-1977 Add ClassNameBuilder.merge

  • Issue Type: FEATURE REQUEST

As a consumer, I should be able to easily merge together the classes and blacklists present in two ClassNameBuilders.

ClassNameBuilder a = new ClassNameBuilder()
  ..add('a')
  ..add('a-blacklist')
  ..add('b-blacklist')
  ..blacklist('a-blacklist');

ClassNameBuilder b = new ClassNameBuilder()
  ..add('b')
  ..add('a-blacklist')
  ..add('b-blacklist')
  ..blacklist('b-blacklist');

b.merge(a);

b.toClassName(); // 'a b'
b.toClassNameBlacklist() // 'a-blacklist b-blacklist'

This should be done efficiently by directly concatenating _classNamesBuffer and _blacklistBuffer, and not using .toClassName()

overReactReduxDevToolsMiddleware needs more informative warning when `toJson` not implemented

  • Issue Type: FEATURE REQUEST
  • over_react Version(s): 3.1.4

When using overReactReduxDevToolsMiddleware with DevToolsStore, if some part of the state, or one of the actions, is not encodable by toJson, the following generic warning is issued:

dart_sdk.js:101547 OverReactReduxDevToolsMiddleware [WARNING]: You must implement a toJson method in your state and actions in order to view state changes in the redux dev tools.

This can be very difficult to track down in a large state tree.

It would be better for the error message to point to the specific object that was not encodable. I have spent a lot of time trying to track it down in various parts of my state tree and actions, when I thought I had implemented toJson properly.

In fact, immediately before I call the constructor for DevToolsStore, I create my state object and call toJson() on it, and it works perfectly, yet I get that warning while the constructor is executing in the next step.


FYI: @greglittlefield-wf @aaronlademann-wf @kealjones-wk @evanweible-wf @maxwellpeterson-wf

cannot return null from connected component

  • Issue Type: [BUG, FEATURE REQUEST, etc.]
  • over_react Version(s): 3.1.7

I expected that since it is legal to return null from the render() method of a normal React component, that it would be legal to return null from the mapStateToProps function. I realize they have different return types, but it seems the meaning should be the same in each case (i.e., don't render anything).

However, it generates this error:

dart_sdk.js:15575 Uncaught TypeError: Cannot read property 'Symbol(dartx.keys)' of null
    at js_backed_map.JsBackedMap.new.addAll (dart_sdk.js:15575)
    at js_backed_map.JsBackedMap.new.addAll (js_backed_map.dart:95)
    at Function.from (js_backed_map.dart:34)
    at Object.jsBackingMapOrJsCopy (js_backed_map.dart:151)
...

The workaround is to render the unconnected version of the component, but pass it a special prop that means "return null immediately".

But the intent would be communicated more straightforwardly if the mapStateToProps function could return null on its own.


FYI: @greglittlefield-wf @aaronlademann-wf @kealjones-wk @evanweible-wf @maxwellpeterson-wf

Remove $Props and $PropKeys

$Props and $PropKeys will not work in Dart 2 and will need to be removed. The alternative API is via static PropsMeta meta getters on props classes.

How can i add animation to a component on load in overreact?

  • Issue Type: Query
  • over_react Version(s): 1.24.1

I need to display a component on a certain condition . The display of component should be done through a sliding effect . Where to write animation so that it works when component is shown on screen .
I tried using main() , constructor() and componentDidMount() . None of the above items are called on load . Can you please suggest ?

FYI: @greglittlefield-wf @aaronlademann-wf @jacehensley-wf @clairesarsam-wf @joelleibow-wf

Flutter-like syntax?

  • Issue Type: [FEATURE REQUEST]

Still learning dart and over_react, but is it possible to create wrapper classes that follow the Flutter API syntax where children is set and no call method is required?

I noticed discussion on issue #41, but can't figure out how it would be implemented. It's just personal preference for structuring the code and saving a few parentheses. Thanks.

@montyclt's example:

new Div(
  prop: 'Value', //named parameter
  children: [
    'child1',
    'child2'
  ]
);

as opposed to:

(Dom.div()
  ..prop = 'Value'
)(
  'child1',
  'child2'
)

FYI: @greglittlefield-wf @aaronlademann-wf @jacehensley-wf @clairesarsam-wf @joelleibow-wf

Add support for JSX

  • Issue Type: Feature Request

Requested by CookieOfFortune on Reddit

He would like to be able to use a JSX syntax instead of the over_react fluent interface.

Here's a link to how TypeScript supports JSX

Seems like this would possibly be something that would require some dartlang sdk changes, but I'd defer to @greglittlefield-wf's opinion on that.

Either way - until we get more than one person asking about it, this is likely icebox.


FYI: @greglittlefield-wf @aaronlademann-wf @jacehensley-wf @clairesarsam-wf @joelleibow-wf

Doesn't build with latest dart beta

The following code

typedef Callback1Arg<T1>(T1 arg1);

/// Generic strongly-typed callback chaining utilities for callbacks with one argument.
class CallbackUtil1Arg<T> extends CallbackUtil<Callback1Arg<T>> {
}

abstract class CallbackUtil<TTypedef extends Function> {
  const CallbackUtil();
}

now fails with

'T' can't be used contravariantly or invariantly in 'CallbackUtil<dynamic Function(T)>'.
Try not using class type parameters in types of formal parameters of function types.dart(wrong_type_parameter_variance_in_superinterface)

Since This feature landed

UIP-2195 Flutter-like?

Curious if there is a way to wrapper or make the API a practical drop-in for flutter api?

Add story for using JS components w/ Dart API

Utilizing existing React JS component is possible via react-dart, but the setup isn't well-documented.

The experience could also be improved by consuming those components with statically-defined over_react props.

Version compatible issue after upgrading to Dart 2

  • Issue Type: BUG
  • over_react Version(s): 1.24.1

After the release of Dart 2 , we tried to migrate our Overreact code with new update . But throwing error like this .
image

Below is the pubspec.yaml file content:
image

Can anyone provide a perfect way which would support overreact with Dart 2 .

Nb: We tried running the overreact code with old dart versions , but was asking for latest version of dart .

FYI: @greglittlefield-wf @aaronlademann-wf @jacehensley-wf @clairesarsam-wf @joelleibow-wf

mapStateToPropsWithOwnProps not called properly on OverReactRedux connected component

  • Issue Type: BUG
  • over_react Version(s): 3.1.4

It appears that mapStateToPropsWithOwnProps is not called (or is called improperly) when passed into the connect function to create a connected component with OverReact Redux.

To reproduce in the OverReact Redux simple example, follow these steps:

  1. Add the following to components/counter.dart:
UiFactory<CounterProps> ConnectedMultiplierCounter = connect<CounterState, CounterProps>(
    mapStateToPropsWithOwnProps: (state, props) =>
        (Counter()..currentCount = state.smallCount * props.multiplier))(Counter);

at the top-level, and add a new prop int multiplier in _$CounterProps.

  1. Add the following lines to (e.g., in between lines 18 and 19) in index.dart:
Dom.h2()('ConnectedMultiplierCounter'),
(ConnectedMultiplierCounter()..multiplier = 7)(),
  1. Add the following lines to the render() method of components/counter.dart:
print('rendering CounterComponent');
print('  multiplier = ${props.multiplier}');
print('  currentCount = ${props.currentCount}');

The following should be printed (in some order) when ConnectedMultiplierCounter is rendered:

rendering CounterComponent
  multiplier = 7
  currentCount = null

whereas the expected behavior is to print

rendering CounterComponent
  multiplier = 7
  currentCount = 7

FYI: @greglittlefield-wf @aaronlademann-wf @kealjones-wk @evanweible-wf @maxwellpeterson-wf

Automatic code migration tools for Dart 1 -> Dart 2

To ease the transition, automatic code migration tools should be provided for:

  • Dart 1 -> Dart 2 (if not concerned with forwards/backwards compatibility)

Or,

  • Dart 1 -> Dart 1 & Dart 2 (forwards- and backwards-compatibility)
  • Dart 1 & Dart 2 -> Dart 2

..if you need to transition multiple packages/codebases without breaking changes.

prevProps do not have generated getters

  • Issue Type: [FEATURE REQUEST]
  • over_react Version(s): ^3.1.0

For implementing functions such as componentDidUpdate and getSnapshotBeforeUpdate and any function that has the parameter prevProps, is there a way to generate getters similar to how getters are generated for UiProps? Currently, my approach to working with prevProps is as follows. Supposed I have a prop:

@Props()
class _$AppProps extends UiProps with ConnectPropsMixin {
  String field;
}

Now suppose I try to implement componentDidUpdate and I want to compare the previous prop's field to the current prop's field:

  @override
  void componentDidUpdate(Map prevProps, Map prevState, [snapshot]) {
    if (props.field != prevProps['AppProps.field']) {
        // -- snip --
    }
  }

I am forced to use a string literal for the prevProps map. Is there a better way to do this? Using these string fields was exactly the kind of problem that OverReact was designed to avoid no?


FYI: @greglittlefield-wf @aaronlademann-wf @kealjones-wk @evanweible-wf @maxwellpeterson-wf

UIP-3018 ResizeSensor intermittent null error in _reset

  • Issue Type: BUG

We're seeing stacktraces sometimes originating from ResizeSensor:

resize_sensor.dart ResizeSensorComponent._reset 
resize_sensor.dart ResizeSensorComponent.componentDidMount.<anonymous function> 
resize_sensor.dart _SafeAnimationFrameMixin.requestAnimationFrame.<anonymous function> 

message: Uncaught exception: NullError: x is null

One potential cause of this is that _reset is being called after the component is unmounted, with the unmount taking place during the animation frame delay.

We should add a _isMounted check inside _reset to hopefully fix this.

UIP-2024 Support covariant props

Dart 1.22 recently added support for covariant overrides, which allows for sound tightening of method argument types.

This applies to fields/setters as well.

Currently, you cannot tighten the type of a prop in strong mode.

class FooProps {
  Iterable items;
}

class BarProps extends FooProps {
  @override
  List items; // Error: Field declaration FooProps.items cannot be overridden in BarProps.
}

However, with covariant, that should now be possible:

class FooProps {
  covariant Iterable items;
}

class BarProps extends FooProps {
  @override
  List items;
}

The transformer needs to be updated to generate setters with the covariant keyword when necessary.

-covariant Iterable items;
+Iterable get items => props[...];
+set items(covariant Iterable value) => props[...] = value;

Also worth noting:

In cases where the supertype authors did not foresee this need, it is still possible to tighten a parameter type by putting the covariant modifier on the overriding parameter declaration.

request to remove built_redux dependency

  • Issue Type: FEATURE REQUEST
  • over_react Version(s): 3.1.4

over_react depends on built_redux, which due to versioning issues means I cannot upgrade to built_value version 7.0.0 or later.

Assuming built_redux is not being used in some crucial way (I saw there were some experiments with supporting it in commits from last year, but I don't see any support in the the current documentation), it would be helpful to remove the dependency so that clients can use the latest version of built_value.

The request at the built_redux repo to upgrade the dependency is 3 weeks old, with no reply, so I'm not confident it will be resolved soon.


FYI: @greglittlefield-wf @aaronlademann-wf @kealjones-wk @evanweible-wf @maxwellpeterson-wf

Using react-bootstrap components with over_react?

Hi,

First of all this seem to be like a really great initiative! Great job especially with supporting dart2 now.

My question is there an easy way of using reactstrap components with over_react?

Accessing createClass via the main React package is deprecated, and will be removed in React v16.0

  • Issue Type: BUG
  • over_react Version(s): 1.29.0
  • SDK: 1.24.3

Warning: Accessing createClass via the main React package is deprecated, and will be removed in React v16.0. Use a plain JavaScript class instead. If you're not yet ready to migrate, create-react-class v15.* is available on npm as a temporary, drop-in replacement. For more info see https://fb.me/react-create-class

It seems that react-dart for Dart 2.1.0 doesn't have this issue. But I dropped back to 1.24.3 for many good plugins. Thanks for your hard work.

UIP-3014 Warn when key uses default toString impl

With key being dynamic and toString-ing values, it's easy to pass in any object.

It's also easy to pass in objects with non-unique toString implementations, such as the default Object.toString, and not notice that the value won't help when keying.

class Foo {
  final bar;
  Foo(this.bar);
}
..key = new Foo(1) // Uses "Instance of 'Foo'" as key
..key = new Foo(2) // Uses "Instance of 'Foo'" as key

We should check whether values like this are specified (perhaps via a regex, like ^Instance of '[_\$a-zA-Z0-9]+'$) and warn in the console.

UIP-2153 Transition warning sometimes fires when it shouldn't

  • Issue Type: BUG
  • over_react Version(s): 1.8.0

The newly-added transition warning,

VALIDATION WARNING: The number of transitions expected to complete have not completed. Something is most likely wrong.

fires when shouldn't sometimes.

For instance, sometimes transitionend events never fire on purpose, such as when the user cancels the transition (think hiding a popover while it's still showing). This results in a warning when it should not.

I also noticed in the code that the timer does not unsubscribe the listener function, which it probably should.


FYI: @greglittlefield-wf @aaronlademann-wf @jacehensley-wf @clairesarsam-wf @joelleibow-wf @kaanaksoy-wk

Error while running tests - Unable to spawn isolate: The built-in library 'dart:html' is not available on the stand-alone VM.

  • Issue Type: BUG,
  • over_react Version(s): 1.18.1

Hi i ran into this issue while running test for the first time on cloning the git repo . i didnt make any changes . Even then on running the tests as mentioned in documentation , wm getting error like

Unable to spawn isolate: The built-in library 'dart:html' is not available on the stand-alone VM.
'package:todo_dart_react/src/todo_dart_react/components.dart': error: line 5 pos 1: library handler failed
import 'dart:html';

What might be the possible reason?. i tried for some solutions . But not working
capture4

Could give more examples?

Could give more examples.

I hope could see following examples:

  • Update states
  • Update states to update sub component props
  • Ref operation
  • event handler ( e.g. onClick )

I have no ideal how to use over_react in real world.

I'm sorry for my bad English.

toJson not implemented on built_collection collections prevents use with Redux dev tools

  • Issue Type: BUG
  • over_react Version(s): 3.1.4

I'm sorry, I'm not quite sure who is "responsible" for this, so it may not be this package, but some combination of built_collection, over_react, and redux.dart is not working for me. The author of built_collection thinks the issue is in the over_react package.

The general case occurs when using over_react, redux.dart, built_collection, and built_value, with a redux state that is a built_value tree composed of other built_value objects recursively, as well as built_collection collections such as BuiltList.

Here's a self-contained example where the whole state is a single BuiltList:

var state = BuiltList<int>([2, 5]);
var store = DevToolsStore<BuiltList<int>>((s, _) => s, initialState: state, middleware: [overReactReduxDevToolsMiddleware]);

This produces the warning

OverReactReduxDevToolsMiddleware [WARNING]: You must implement a toJson method in your state and actions in order to view state changes in the redux dev tools.

The warning is accurate; Redux DevTools is not able to display the state in the Chrome plugin and instead displays

(pin): "Could not encode state. Ensure state is json encodable"

I tried adding the following code to enable BuiltList to have a toJson method, but the issue remains.

extension BuiltListSerializable<E> on BuiltList<E> {
  dynamic toJson() => this.toList();
}

The error does not occur with built_value objects, because I define those myself (unlike BuiltList, BuiltMap, BuiltSet) and can add a toJson method to them. To do this I define a mixin as follows:

@SerializersFor([
  // big list of all my built_value classes
])
Serializers serializers = _$serializers;
Serializers standard_serializers = (serializers.toBuilder()..addPlugin(new StandardJsonPlugin())).build();

mixin BuiltJsonSerializable {
  dynamic toJson() => standard_serializers.serialize(this);
}

FYI: @greglittlefield-wf @aaronlademann-wf @kealjones-wk @evanweible-wf @maxwellpeterson-wf

connect creates new Props map object on every Action dispatch, which must be deeply compared

  • Issue Type: FEATURE REQUEST
  • over_react Version(s): 3.1.5

I think that the performance of the connect function in OverReact redux, specifically the performance of the functions it sets up such as handleAreOwnPropsEqual, handleAreStatePropsEqual, etc. is adversely affected by potentially unnecessarily equality testing.

I can't set up a simple example to show the effect. It's just something I notice in my complex app with fairly large State. I'm trying to do very frequent updates (drawing a box created by the user click-dragging), 60 times per second. It can barely render more than once or twice a second when the State is large, even though it is only updating one tiny little object in the State tree representing the dimensions of the box.

I've done the recommended React/Redux optimizations such as setting up connect on components that would take a while to render, and writing shouldComponentUpdate on unconnected components. I've profiled with React DevTools and I know that no components are re-rendering that don't need to, so I know it is not unnecessary re-renders making it slow. According to React DevTools, the total render time for everything is very fast (about 1 ms in each round up updating).

The Chrome performance profiler is tough to pick apart, because it points to execution in dart_sdk.js, and who knows which Dart code caused that call.

But the profiler tells me that each round of updates takes at least 100 ms (preventing 60 Hz rendering), and 70% of this time is being spent in _shallowMapEquality in src\over_react_redux\over_react_redux.dart. I stepped through and found that (a representation of) my entire State tree is being deeply compared for equality using this function every time any Action is dispatched.

I was very careful to set up an immutable State using built_value and built_collection, only generating a new State (or new part of a subtree of the State) if something changes. I even re-wrote parts of built_value to cache hashCode so that it is not re-computed unnecessarily. I am also sure that my reducers, when they don't change any data, return the same object, so for such objects an equality comparison should be a one-line call to identical(this, other).

But somewhere in the depths of OverReact Redux, I think these carefully constructed objects are being translated into Js-backed Maps, where two different map objects exist that represent the same data, and they need to be deeply compared all the way down to the leaves, just to check that they are equal.

I could be wrong about this, because I don't really understand the architecture of OverReact Redux. But perhaps there is a way to do some optimizations within OverReact Redux so that when data in most of the State tree does not change, the same objects are kept in place so equality testing is a fast call to identical and does not need to recurse deeply.

Alternately, do you have a suggestion for how to structure my code to prevent such unnecessary equality checking?


FYI: @greglittlefield-wf @aaronlademann-wf @kealjones-wk @evanweible-wf @maxwellpeterson-wf

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.