Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CPLAT-4303: React 16 New Context #160

Merged

Conversation

kealjones-wk
Copy link
Collaborator

@kealjones-wk kealjones-wk commented Feb 28, 2019

We may delay this PR until we start work on ComponentV2 and then implement it in ComponentV2 and put Legacy Context back in Component but this pr is ready for an implementation CR with this in mind.

Merge conflicts are to be expected in the lib folders .js and .map files these are generated from the js_src files and running npm run build. They will be resolved with a merge of the js_src folders and a new build on the wip branch.

Description:

React 16 implements a better New Context API.

Changes:

  • Added createContext to react_client.dart
  • Added contextType to react.Component2
  • Added ReactDartContext that utilize interop ReactContext for use in ReactJS
  • Added ReactJsComponentFactoryProxy that was originally created by @greglittlefield-wf about 2 years ago, then added a ReactJsContextComponentFactoryProxy for use with react16 and context components.
  • Add tests for ReactJsComponentFactoryProxy
  • Changes type checking test that was used for JsBackedMaps to a shared suite, which was then also used for context.
  • Updated tests that previously skipped thought to be deprecated lifecycle method shouldComponentUpdateWithContext
  • Add tests for calculateChangedBits argument of context.
  • Update getSnapshotBeforeUpdate tests to use new shared type tests
  • Removes js_util deprecations and todos from Deprecate Original JS Interop Helpers CPLAT-4909 #171

Testing suggestions:

Potential areas of regression:

greglittlefield-wf and others added 30 commits December 29, 2016 16:29
# Conflicts:
#	test/js_interop_helpers_test/shared_tests.dart
* master:
  Bump version meta for 4.6.1 release
  Use isInstanceOf instead of TypeMatcher
  widen build package ranges
  type style map
Copy link
Collaborator

@aaronlademann-wf aaronlademann-wf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kealjones-wk didn't make it all the way through, have a parent teacher conference for Noah this morning at 8:30. Will continue taking a look when I return.

lib/react_client.dart Outdated Show resolved Hide resolved
example/js_components/js_components.dart Show resolved Hide resolved
if (nextValue['renderCount'] % 2 == 0) {
result |= 1 << 2;
}
return result;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have literally no idea what is happening, or why it is happening. The second argument of createContext deserves some documentation explaining what it does. For instance, why does it always return an integer?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The React team doesn't even have documentation around it. They act as if it doesn't exist even though it obviously does, i think this may still be a WIP/secret or experimental feature...

The idea is that you return a bit that indicates which parts/or how the context value changed, then you can use the observedBits props on a consumer and they will only re-render if the bits they are observing are returned from calculateChangedBits

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the crazy delayed reply here @kealjones-wk - a doc comment about how you perceive it to work (like what you just typed above) would be a great addition!


class _NewContextConsumerComponent extends react.Component2 {
render() {
return TestNewContext.Consumer({'unstable_observedBits': props['unstable_observedBits']}, (value) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we using unstable ReactJS APIs in something extending from Component2?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just an example but, the unstable_observedBits prop functions better than the stable version of the prop: observedBits... thats why its used here

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delayed reply.

I get that its just an example... but it is an example intended to exercise the functionality in-lieu of a formal functional test - which is why I'm concerned about our implementation possibly not functioning well with the stable version of observedBits.

example/test/react_test_components.dart Show resolved Hide resolved
js_src/_dart_helpers.js Outdated Show resolved Hide resolved
lib/react.dart Show resolved Hide resolved
lib/react.dart Outdated Show resolved Hide resolved
lib/react.dart Show resolved Hide resolved
lib/react.dart Outdated Show resolved Hide resolved
lib/react.dart Outdated Show resolved Hide resolved
lib/react.dart Outdated Show resolved Hide resolved
lib/react.dart Outdated Show resolved Hide resolved
lib/react.dart Outdated Show resolved Hide resolved
lib/react_client.dart Show resolved Hide resolved
lib/react_client.dart Show resolved Hide resolved
lib/react_client.dart Show resolved Hide resolved
lib/src/react_client/context.dart Outdated Show resolved Hide resolved
test/factory/common_factory_tests.dart Outdated Show resolved Hide resolved
js_src/_dart_helpers.js Outdated Show resolved Hide resolved
lib/react.dart Outdated Show resolved Hide resolved
lib/react.dart Outdated Show resolved Hide resolved
lib/react.dart Outdated Show resolved Hide resolved
lib/react.dart Outdated Show resolved Hide resolved
test/lifecycle_test.dart Outdated Show resolved Hide resolved
test/lifecycle_test.dart Outdated Show resolved Hide resolved
test/lifecycle_test.dart Outdated Show resolved Hide resolved
test/lifecycle_test/component2.dart Outdated Show resolved Hide resolved
@@ -23,181 +24,17 @@ main() {
JsBackedMap jsBackedMap;
JsBackedMap dynamicJsBackedMap;

setUp(() {
testTypeValue(testValue) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type information of testValue is no longer being propagated. Are we sure that this test is equivalent to the old one? @greglittlefield-wf

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking about this and we may be able to use generics to propagate the static type information, and use JS inlining annotations from package:meta to force dart2js to use that type info (if it doesn't already):

Suggested change
testTypeValue(testValue) {
@tryInline
void testTypeValue<T>(T testValue) {

I'll give that a shot locally.

Related: we should update the builder config for this repo to use the Dart 2 equivalent of the trust-type-annotations flag, since that's what we'll be using in prod most of the time.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks manually inlining doesn't do much for the dart2js output, but we should add the generics so that both tests aren't essentially just as dynamic.

I'll make a PR to add those and also add the build.yaml and test changes

import 'package:js/js.dart';
import 'package:test/test.dart';

sharedTypeTests(Function testTypeValue,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This typing should be more specific:

Suggested change
sharedTypeTests(Function testTypeValue,
typedef void TestTypeValue<T>(T value);
void sharedTypeTests(TestTypeValue testTypeValue,

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should avoid unnecessary typedefs since we're Dart 2 only now... we can just use the new inline Function syntax.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point; the only reason I put in the typedef was because I wasn't sure if the semantics were the same for generics

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in kealjones-wk#3

test/shared_type_tester.dart Outdated Show resolved Hide resolved
test/react_context_test.dart Show resolved Hide resolved
test/react_context_test.dart Show resolved Hide resolved
lib/react.dart Outdated Show resolved Hide resolved
lib/react.dart Show resolved Hide resolved
expect(consumerRef.latestValue, same(typeToTest));
}

group('New Context API (Component2 only)', () {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there any tests that the correct nextContext arg is passed into shouldComponentUpdate? I can't seem to find any. If not, could you add some?

lib/react.dart Outdated Show resolved Hide resolved
Copy link
Collaborator

@aaronlademann-wf aaronlademann-wf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

Really nice work on this @kealjones-wk !!!

Copy link
Collaborator

@greglittlefield-wf greglittlefield-wf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some things we should address in a follow-up PR, but are good for now:

  • Comments in this review
  • Comment about testing the nextContext value passed into shouldComponentUpdate (the only test we have uses null; we should also have a test with another value)
    The JS components demo fails in release mode:
    js_components.dart.js:9006 Uncaught ReferenceError: ui is not defined
        at js_components.dart.js:9006
        at Object.holder.(anonymous function) (http://localhost:8080/js_components/js_components.dart.js:105:35)
        at _IndexComponent.render$0 (js_components.dart.js:8492)
        at _handleRender_closure.call$0 (js_components.dart.js:8249)
        at _RootZone.run$1$1 (js_components.dart.js:5644)
        at _RootZone.run$1 (js_components.dart.js:5648)
        at closure_handleRender.call$4 (js_components.dart.js:8240)
        at Object.Primitives_applyFunction (js_components.dart.js:457)
        at Object.Function_apply (js_components.dart.js:2451)
        at _callDartFunctionFast (js_components.dart.js:2791)
    

This is awesome!!!

+10


/// Prefix to namespace react-dart symbols
var _reactDartSymbolPrefix = 'react-dart.';
/// A global symbol to identify javascript objects owned by react-dart context,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#nit

Suggested change
/// A global symbol to identify javascript objects owned by react-dart context,
/// A symbol to identify JavaScript objects owned by react-dart context,

}

bool handleShouldComponentUpdate(Component2 component, JsMap jsNextProps, JsMap jsNextState) => zone.run(() {
bool handleShouldComponentUpdate(Component2 component, JsMap jsNextProps, JsMap jsNextState,
[dynamic jsNextContext]) =>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't be optional, right?

@@ -587,25 +612,117 @@ ReactDartComponentFactoryProxy _registerComponent(ComponentFactory componentFact
return new ReactDartComponentFactoryProxy(reactComponentClass);
}

class _ReactJsContextComponentFactoryProxy extends ReactJsComponentFactoryProxy {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could use a doc comment

@greglittlefield-wf greglittlefield-wf merged commit 7c14909 into Workiva:5.0.0-wip Apr 3, 2019
@kealjones-wk
Copy link
Collaborator Author

kealjones-wk commented Apr 3, 2019

  • Comment about testing the nextContext value passed into shouldComponentUpdate (the only test we have uses null; we should also have a test with another value)

@greglittlefield-wf if the only test you're referring to is this one: https://github.com/cleandart/react-dart/pull/160/files#diff-c00a02aa016b25a86fb30a2093f935a1R520

because that component it is a child of a context.consumer callback and not a consumer/contextType component itself it will actually never have a this.context the only time this.context ever has a value is when contextType is set, and when contextType is set context and prop updates don't trigger shouldComponentUpdate.

The only time i have seen shouldComponentUpdate and nextContext be called and have a value is on a component using contextType that has a state change. But this nextContext is no different than this.context at this point so its utterly useless. but we could totally add another test to test that really awkward setup to make sure it's working.

@greglittlefield-wf
Copy link
Collaborator

But this nextContext is no different than this.context at this point so its utterly useless.

Yeah, I think we just need to verify that when it is passed in, that it's valid.

@aaronlademann-wf aaronlademann-wf added this to the 5.0.0 milestone Apr 4, 2019
@aaronlademann-wf aaronlademann-wf modified the milestones: 5.1.0, 5.0.0 May 1, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants