-
Notifications
You must be signed in to change notification settings - Fork 47.1k
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
Unperformant Stringify Warning - WIP #12263
Unperformant Stringify Warning - WIP #12263
Conversation
…e values and warns if they take too long
@Swieckowski
|
In the simplest terms, if I run yarn test I'll fail a bunch of tests with |
I see, thx for explanation. |
Node |
} else { | ||
node.setAttribute(attributeName, '' + (value: any)); | ||
node.setAttribute(attributeName, (value: any).toString()); |
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.
isn't this will throw on undefined
value? we only strictly check for null
above it.
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.
Yeah, @Swieckowski please revert this. We were intentionally not using toString
Can we do check if
|
it('should warn if custom attributes take too long to stringify', () => { | ||
const container = document.createElement('div'); | ||
const attributeValue = {foo: 'bar'}; | ||
attributeValue.toString = function() { |
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.
IMO, We don't need actual implementation (which will slow down the test suite). I think better do polyfill global.performance
in the test setup and implement our own version of it (like @aweary mentioned in #12209 (comment)).
For an idea, we can take a look at
react/packages/react-dom/src/__tests__/ReactDOMRoot-test.internal.js
Lines 49 to 53 in 2cf9063
global.performance = { | |
now() { | |
return now; | |
}, | |
}; |
or jest.mockImplementation
might be helpful.
Hope this help :)
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.
Yeah, let's go ahead and go with mocking performance
instead of actually taking too much time.
// Only used in DEV, stringifies a value and Warns if it took too long | ||
const stringifyWithPerformanceWarning = value => { | ||
const stringifyStart = Date.now(); | ||
let attributeValue = (value: any).toString(); |
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.
Use const
over let
.
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.
Please use '' + value
instead of toString
here as well.
|
||
warning( | ||
stringifyEnd - stringifyStart <= 2, | ||
'Stringifying your attribute is causing perfomance issues', |
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.
Typo: pefomance => performance.
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.
The messaging here isn't clear enough. Let's also pass the attribute name to stringifyWithPerformanceWarning
, improve the wording, and also include a component stack. You can get a component stack by importing and using getCurrentFiberStackAddendum
from ReactDebugCurrentFiber
.
Lets make it look like:
warning(
stringifyEnd - stringifyStart <= 2,
'The attribute `%s` took more than 2 milliseconds to stringify. This usually means you provided a large object ' +
'as the value for a DOM attribute, which can lead to performance issues.%s',
attributeName,
getCurrentFiberStackAddendum(),
)
} else { | ||
// `setAttribute` with objects becomes only `[object]` in IE8/9, | ||
// ('' + value) makes it output the correct toString()-value. | ||
attributeValue = '' + (value: any); | ||
attributeValue = (value: any).toString(); |
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.
Why was this changed? The comment above no longer reflects the actual code. Aren't we concerned about IE8/9 anymore? If not, then the comment should also be updated, I guess.
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.
https://reactjs.org/blog/2016/01/12/discontinuing-ie8-support.html
Just IE9+ it seems
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.
@Swieckowski please revert his back as well.
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.
Sorry for the late review, and thanks again for doing this @Swieckowski. Please see my comments for what we need to change.
it('should warn if custom attributes take too long to stringify', () => { | ||
const container = document.createElement('div'); | ||
const attributeValue = {foo: 'bar'}; | ||
attributeValue.toString = function() { |
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.
Yeah, let's go ahead and go with mocking performance
instead of actually taking too much time.
} else { | ||
node.setAttribute(attributeName, '' + (value: any)); | ||
node.setAttribute(attributeName, (value: any).toString()); |
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.
Yeah, @Swieckowski please revert this. We were intentionally not using toString
} else { | ||
// `setAttribute` with objects becomes only `[object]` in IE8/9, | ||
// ('' + value) makes it output the correct toString()-value. | ||
attributeValue = '' + (value: any); | ||
attributeValue = (value: any).toString(); |
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.
@Swieckowski please revert his back as well.
@@ -172,3 +180,17 @@ export function setValueForProperty( | |||
} | |||
} | |||
} | |||
|
|||
// Only used in DEV, stringifies a value and Warns if it took too long | |||
const stringifyWithPerformanceWarning = value => { |
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.
For consistency let's move this to the top of the file, and then only define it in DEV
let stringifyWithPerformanceWarning;
if (__DEV__) {
stringifyWithPerformanceWarning = function(value) {
// ...
}
}
``
// Only used in DEV, stringifies a value and Warns if it took too long | ||
const stringifyWithPerformanceWarning = value => { | ||
const stringifyStart = Date.now(); | ||
let attributeValue = (value: any).toString(); |
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.
Please use '' + value
instead of toString
here as well.
|
||
// Only used in DEV, stringifies a value and Warns if it took too long | ||
const stringifyWithPerformanceWarning = value => { | ||
const stringifyStart = Date.now(); |
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 want to use performance.now
if it exists, and fallback to Date.now
if it doesn't. Lets do the same thing we do here: https://github.com/facebook/react/blob/master/packages/react-native-renderer/src/ReactNativeFrameScheduling.js#L12-L17
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.
Thanks a lot for the review, I'll get on it sometime soon!
|
||
warning( | ||
stringifyEnd - stringifyStart <= 2, | ||
'Stringifying your attribute is causing perfomance issues', |
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.
The messaging here isn't clear enough. Let's also pass the attribute name to stringifyWithPerformanceWarning
, improve the wording, and also include a component stack. You can get a component stack by importing and using getCurrentFiberStackAddendum
from ReactDebugCurrentFiber
.
Lets make it look like:
warning(
stringifyEnd - stringifyStart <= 2,
'The attribute `%s` took more than 2 milliseconds to stringify. This usually means you provided a large object ' +
'as the value for a DOM attribute, which can lead to performance issues.%s',
attributeName,
getCurrentFiberStackAddendum(),
)
One more concern is that |
We'll only be showing the warning in development mode. Performance drops in development should be insignificant. |
This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contribution. |
Closing this pull request after a prolonged period of inactivity. If this issue is still present in the latest release, please ask for this pull request to be reopened. Thank you! |
Closes #12209.
performance.now()
was not supported while I was writing this, so I'm using the less preciseDate.now()
.I have written a test to see if the warning shows up if something takes too long to stringify, but don't have one for if it does not warn, since I didn't see anyone else test for anything like that.