-
Notifications
You must be signed in to change notification settings - Fork 47.4k
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
Conversation
rricard
commented
Oct 30, 2016
•
edited
Loading
edited
- 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
Will keep |
@@ -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); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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...
There was a problem hiding this comment.
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 ...
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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))) { |
There was a problem hiding this comment.
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`); |
There was a problem hiding this comment.
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.
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). |
@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... |
@rricard If we dedupe, it should be deuping based on the class (the 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. |
@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; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
@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`); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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))) { |
There was a problem hiding this comment.
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?
Well, I can't explain why the last build failed... |
Maybe it ran out of memory generating coverage or something like this. |
@gaearon happened on an another build: https://travis-ci.org/facebook/react/builds/172282875 |
I just found the errors are caused by a new script for tracking fiber errors.
This will update the file tracking passing tests so we know which exactly are passing. |
@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 |
There was a problem hiding this comment.
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.
Can you rebase and we'll see if it passes the Travis CI tests? |
Rejects Arrays, Strings & Numbers. Allows nulls.
- check the absence of getInitialState & getDefaultProps as methods - check the absence of propTypes & contextTypes at instance-level
Avoid rechecking while resuming
This is achieved by tracking the warning state in the Fiber structure.
@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 { |
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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.
* 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