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

[Fiber] Complete ES6 Class related errors support #8156

Merged
merged 15 commits into from
Nov 5, 2016
Merged

[Fiber] Complete ES6 Class related errors support #8156

merged 15 commits into from
Nov 5, 2016

Conversation

rricard
Copy link
Contributor

@rricard rricard commented Oct 30, 2016

  • Print a warning in fiber for lacking render method
  • Throw with non-object in the initial state property
  • Classic & instance property checks
  • Typos in lifecycle methods

@rricard
Copy link
Contributor Author

rricard commented Oct 30, 2016

Will keep ReactES6Class › renders based on context in the constructor & ReactES6Class › supports this.context passed via getChildContext aside for now, will work later on a dedicated PR for context transmission.

@rricard rricard changed the title [Work In Progress][Fiber] Complete ES6 Class Support [Fiber] Complete ES6 Class related errors support Oct 30, 2016
@@ -107,6 +177,7 @@ module.exports = function(scheduleUpdate : (fiber: Fiber, priorityLevel : Priori
const ctor = workInProgress.type;
const props = workInProgress.pendingProps;
const instance = new ctor(props);
checkClassInstance(workInProgress, instance);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we avoid checking if we are resuming work and recreating the instance? Check should only be done once.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Now, That's the interesting part! I'll try to do that...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It wasn't that hard ... Maybe there is a more elegant way to do that though ...

Copy link
Collaborator

Choose a reason for hiding this comment

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

Actually, this is not sufficient since the state may depend on props so it can yield different results during the rerender. This would then only throw an error during race conditions.

class Foo extends React.Component {
  state = this.props.bar ? {} : [];
}

I'd rather check it every time.

For warnings, we should probably ideally dedupe though.

function adoptClassInstance(workInProgress : Fiber, instance : any) : void {
instance.updater = updater;
workInProgress.stateNode = instance;
// The instance needs access to the fiber so that it can schedule updates
ReactInstanceMap.set(instance, workInProgress);
}

function constructClassInstance(workInProgress : Fiber) : any {
function constructClassInstance(workInProgress : Fiber, resuming?: boolean) : any {
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 not be using optional arguments but I'd rather restructure this to not use runtime flags.

@@ -107,6 +177,7 @@ module.exports = function(scheduleUpdate : (fiber: Fiber, priorityLevel : Priori
const ctor = workInProgress.type;
const props = workInProgress.pendingProps;
const instance = new ctor(props);
checkClassInstance(workInProgress, instance);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Actually, this is not sufficient since the state may depend on props so it can yield different results during the rerender. This would then only throw an error during race conditions.

class Foo extends React.Component {
  state = this.props.bar ? {} : [];
}

I'd rather check it every time.

For warnings, we should probably ideally dedupe though.

);
}

if (inst.state && (typeof inst.state !== 'object' || Array.isArray(inst.state))) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you move isArray to the module scope like this?

const isArray = Array.isArray;

}

if (inst.state && (typeof inst.state !== 'object' || Array.isArray(inst.state))) {
throw new Error(`${name}.state: must be set to an object or null`);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you add a comment like // TODO: Change this to be a warning.?

I don't think this needs to be an error but might as well do this for compatibility for now.

@rricard
Copy link
Contributor Author

rricard commented Oct 30, 2016

Same on this PR even if there is less work left, I need to take the time to do things correctly (warning dedupe for instance).

@rricard
Copy link
Contributor Author

rricard commented Oct 30, 2016

@sebmarkbage had to add a field in the Fiber struct in order to avoid warning twice. Maybe you have a better idea in order to avoid that though...

@sebmarkbage
Copy link
Collaborator

@rricard If we dedupe, it should be deuping based on the class (the type field), not the fiber instance. It is not necessary to receive it multiple times for the same class.

However, I don't think the stack reconciler does this. So we can leave that problem for later. NBD. I'd just remove the deduplication so we can land the first version of this.

@rricard
Copy link
Contributor Author

rricard commented Oct 31, 2016

@sebmarkbage removed dedupe!

@@ -96,6 +99,90 @@ module.exports = function(scheduleUpdate : (fiber: Fiber, priorityLevel : Priori
return true;
}

function checkClassInstance(workInProgress: Fiber, inst: any) {
const type = workInProgress.type;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we please not calculate these fields in production if we're not planning to print anything?

It's fine to extract this to getFriendlyName(inst) and only call this in __DEV__ branch and in both-dev-and-prod if () branch below when we're sure we'll print the error.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right, I'm going to change that

@rricard
Copy link
Contributor Author

rricard commented Oct 31, 2016

@gaearon Refactored in a getName helper function


if (inst.state && (typeof inst.state !== 'object' || isArray(inst.state))) {
// TODO: Change this to be a warning.
throw new Error(`${getName(workInProgress, inst)}.state: must be set to an object or null`);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you please use invariant module for this? Otherwise the error text won't be automatically stripped out in production builds.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, so that's why you have both! Invariant throws, warning goes in the console... Will do!

);
}

if (inst.state && (typeof inst.state !== 'object' || isArray(inst.state))) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nitpicking but let's read inst.state once?

@rricard
Copy link
Contributor Author

rricard commented Nov 1, 2016

Well, I can't explain why the last build failed...

@gaearon
Copy link
Collaborator

gaearon commented Nov 1, 2016

Maybe it ran out of memory generating coverage or something like this.

@gaearon gaearon self-assigned this Nov 1, 2016
@rricard
Copy link
Contributor Author

rricard commented Nov 1, 2016

@gaearon happened on an another build: https://travis-ci.org/facebook/react/builds/172282875

@gaearon
Copy link
Collaborator

gaearon commented Nov 1, 2016

I just found the errors are caused by a new script for tracking fiber errors.
Please rebase on master and then run

./scripts/fiber/record-tests

This will update the file tracking passing tests so we know which exactly are passing.

@rricard
Copy link
Contributor Author

rricard commented Nov 2, 2016

@gaearon done!

@@ -87,6 +72,9 @@ src/renderers/art/__tests__/ReactART-test.js
* resolves refs before componentDidMount
* resolves refs before componentDidUpdate

src/renderers/dom/__tests__/ReactDOMProduction-test.js
* should throw with an error code in production
Copy link
Collaborator

Choose a reason for hiding this comment

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

Hm. This is strange that this was passing before. This is probably a behavior change that we want to allow.

@sebmarkbage
Copy link
Collaborator

Can you rebase and we'll see if it passes the Travis CI tests?

@rricard
Copy link
Contributor Author

rricard commented Nov 5, 2016

@sebmarkbage you were right, this should be fixed now!

@@ -91,6 +96,98 @@ module.exports = function(scheduleUpdate : (fiber: Fiber) => void) {
return true;
}

function getName(workInProgress: Fiber, inst: any): string {
Copy link
Collaborator

Choose a reason for hiding this comment

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

We have a module that lets you do this now. getComponentName.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I guess that module doesn't use inst.constructor as a fallback. Hm. Maybe it should? Don't know. I'll land this for now while we figure that out.

@sebmarkbage sebmarkbage merged commit ba6b53a into facebook:master Nov 5, 2016
acusti pushed a commit to brandcast/react that referenced this pull request Mar 15, 2017
* Print a warning in fiber for lacking render method

* Reject initial non-object state in Fiber

Rejects Arrays, Strings & Numbers. Allows nulls.

* Centralize fiber checks on ES6 Classes

* Add classic & instance property checks on fiber

- check the absence of getInitialState & getDefaultProps as methods
- check the absence of propTypes & contextTypes at instance-level

* Convert to normalized React `warning` calls

* Support lifecycle typo detection in fiber

* Get the complete warnings from the existing code

* Only check classInstance once

Avoid rechecking while resuming

* Can warn component while resuming but only once

This is achieved by tracking the warning state in the Fiber structure.

* Remove warning deduplication

* Factor name retrieval in a getName helper

* Use invariant instead of throw

* Read inst.state only once for the invariant

* Fix condition on the instance state

* Register failing/passing fiber tests
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants