-
Notifications
You must be signed in to change notification settings - Fork 679
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
Fix conditionals in JSX #2200
Fix conditionals in JSX #2200
Conversation
|
@@ -39,14 +39,14 @@ export default props => { | |||
const cards = getGiftCards(props.data); | |||
|
|||
return cards.value ? ( | |||
<> | |||
<Fragment> |
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.
Is this preferred over <>
? Why?
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.
Yes, it's a bit more explicit and portable. Generally, you can paste our code into a sandbox and it'll work. This particular transform isn't as popular as you might think.
Also, we've used Fragment
almost everywhere else.
@@ -24,43 +24,46 @@ const ShippingMethods = props => { | |||
|
|||
const classes = mergeClasses(defaultClasses, props.classes); | |||
|
|||
const radios = | |||
isShowingForm && hasMethods ? ( |
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.
How about using useMemo
here? @sirugh and I had a conversation yesterday about this. He can expand more on the useMemo
use case.
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 to draw the line somewhere on memoization, and in this codebase, I've generally drawn that line at element creation. The only place we should be memoizing element creation (afaik) is when we're doing it in a loop.
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.
when we're doing it in a loop
Like...a render loop? 😆
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.
But seriously, imagine a toggleable element that we expect a user to interact with quite often. Shouldn't we memoize the state to avoid re-creating the component unnecessarily?
This is the difference we're talking about, I think. Which of these is "more performant"/"better"?
const MyComponent = ({ renderChildren }) => {
const FooComponent = useMemo(() => {
return renderChildren ? (
<Foo />
) : null;
});
const BarComponent = renderChildren ? (
<Bar />
) : null
return (
<Fragment>
{FooComponent}
{BarComponent}
</Fragment>
)
}
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, I feel the same. useMemo
can save on garbage collection if we are smart about the dependency array element. useMemo
can quickly go out of hand if we abuse it by putting too many elements in the dependency array or elements that have a wide range of possible values. I don't think there is an eslint rule that can help us in this case but I feel useMemo
has more potential that we are not using at this point in our code base.
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.
This is the difference we're talking about, I think. Which of these is "more performant"/"better"?
I feel
useMemo
has more potential that we are not using at this point in our code base.
Yes, I understand. The difference here is negligible—it's one createElement()
call. The benefit is not zero, but it's insignificant.
const MyComponent = props => {
// this is also a `createElement` call
// should we memoize it too?
return <div>{props.text}</div>
}
If we wanted to realize the full potential of memoization, we could reproduce PureComponent
.
// let's create a custom hook
// it'll only re-render when props change
const usePureComponent = (Component, props) => useMemo(
() => <Component {...props} />,
[props]
)
// looks pure to me
const MyComponent = props => {
return <div>{props.text}</div>
}
// neat
export default const pure = props => usePureComponent(MyComponent, props)
This is cool, but it's overkill. We're not gaining much from the memoization, and now we're doing something that's pretty unusual. So this is where we draw the line.
We use it to avoid identities changing across renders, and as an early optimization when mapping props to elements in a loop, but not for literally every createElement
call that happens to be amenable to memoization.
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.
But seriously, imagine a toggleable element that we expect a user to interact with quite often. Shouldn't we memoize the state to avoid re-creating the component unnecessarily?
Situationally, any time you have a particularly complex tree, you can consider memoizing it to improve the render performance. Toggling doesn't factor into it much, 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.
A question @revanth0212 had in the office was why not just memoize all the time since you may not know if a tree is complex or not.
<h3>There are no items in your cart.</h3> | ||
); | ||
|
||
const priceAdjustments = hasItems ? <PriceAdjustments /> : 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.
Same here. Can we use useMemo
? At most we will only be caching 2 versions of priceAdjustments
either the component or null.
Description
In this codebase, we avoid multiline statements in JSX because they defeat the main purpose of using JSX: readable trees. In practice, this only comes up when conditionally rendering JSX elements. We've established a pattern of first defining conditional elements as variables (constants), then inserting those variables into the final tree, and we've been pretty good about using this pattern. But we haven't been perfect, and that makes the pattern unclear.
This PR corrects all known cases in which we were not adhering to this pattern.
Related Issue
PWA-415
Acceptance
Verification Steps
yarn test
Screenshots / Screen Captures (if appropriate)
Checklist