-
Notifications
You must be signed in to change notification settings - Fork 4.7k
Avoid rerending the current block if the previous block change #12379
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
Avoid rerending the current block if the previous block change #12379
Conversation
packages/blocks/src/api/utils.js
Outdated
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.
Since we're using strings now for RichText values, the isEqual is useless I think.
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.
=== will check that the values are of the same type and that their values match, so it'll work nicely in the context of two strings, yes.
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.
Since we're using strings now for RichText values, the
isEqualis useless I think.
I also don't think it's something we should have ever wanted, even for non-primitive types, since even if the values are deeply equal, the test seems like it should be more concerned about whether the value has ever changed from its default-assigned reference.
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 value has ever changed from its default-assigned reference.
The reason was: you type in a paragraph, you backspace, you click outside of the block. You have an empty paragraph but a new reference.
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.
And to me I'd be fine to call that "modified" 🤷♂️
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.
As discussed here #11899 (comment) this means we now show the inserter even if the previous block is empty.
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.
Nice removal! Should be an excellent improvement not to load two blocks every single time.
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.
Internal filter not useful anymore because we got rid of "layout" attributes.
sgomes
left a comment
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 code changes look good to me. Great work, @youknowriad! This is an excellent example of where a small UX compromise can lead to a big performance boost.
I'll try to replicate your results in the meantime, but code-wise looks good to merge 👍
|
I see a similar improvement (23%) in my machine 👍 |
849c449 to
c3987ff
Compare
aduth
left a comment
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 like this in an effort of reducing the unpredictability around when and where a sibling inserter is shown. One thing I might worry about is: Before we'd never shown a sibling inserter around an empty default block at all, and now we show it after but not before an empty default block. It seems to me we'd probably be better off one way or the other. In other words, do we even need this canShowInBetweenInserter at all ?
packages/blocks/src/api/utils.js
Outdated
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.
Since we're using strings now for RichText values, the
isEqualis useless I think.
I also don't think it's something we should have ever wanted, even for non-primitive types, since even if the values are deeply equal, the test seems like it should be more concerned about whether the value has ever changed from its default-assigned reference.
| const { clientId, previousBlockClientId, nextBlockClientId, onMerge } = this.props; | ||
|
|
||
| const { clientId, getPreviousBlockClientId, getNextBlockClientId, onMerge } = this.props; | ||
| const previousBlockClientId = getPreviousBlockClientId( clientId ); |
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.
Another thing which will be nice to move to the dispatch handler when #11851 lands. Or, at least, our non-conventional use of selectors as functions directly within the component is concerning (but largely in its being unconventional).
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.
Or, at least, our non-conventional use of selectors as functions directly within the component is concerning (but largely in its being unconventional).
On further reflection, I think I could articulate my concern better in being a diminished distinction between withSelect as serving to provide data and withDispatch as serving to provide behavior, where what we've done here is more in the realm of a behavior.
To clarify, not something which needs to be addressed here.
| isSelected, | ||
| isParentOfSelectedBlock, | ||
|
|
||
| // We only care about this value when the shift key is pressed. |
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 comment is no longer strictly applicable to the use-case, since there's now a second use-case.
|
|
||
| return every( attributeKeys, ( key ) => | ||
| isEqual( newDefaultBlock.attributes[ key ], block.attributes[ key ] ) | ||
| return every( blockType.attributes, ( value, key ) => |
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.
There's a subtlety here in how we use the block type's attributes definition instead of the actual default block we've created; I assume that's so we account for keys which don't have a default assigned and thus would be absent from the default block, but potentially present in the block being tested?
Some comment to this effect could help clarify for future maintainers.
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.
My thinking is that the previous implementation was needed because undeclared attributes were persisted initially, but that's not the case anymore.
| return false; | ||
| } | ||
|
|
||
| const newDefaultBlock = createBlock( defaultBlockName ); |
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.
Maybe a red herring, but createBlock seems like something which in its current state is somewhat non-trivial, at least if we expect it to be called frequently. Do we need to create a new instance of the block each time this function is called?
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 need the "normalized" attributes (default values), An alternative would be to avoid this behavior "isUnmodified" if the default block has been changed. And just rely on very quick but static checks as we know the block is "paragraph".
I don't know, I'll explore it separately :) |
|
or actually, I'll update this PR iff it seems useless. |
My worry is that the pull request in its current form is the worst of all options, between:
|
|
I pushed a commit to always show the in-between inserter. |
FYI @jasmussen |
|
The insertion point line is not shown when hovering a block option in the inserter before an empty block. Am I correct in assuming that this would be resolved by #12384 ? |
|
I think that's correct @aduth now that you mention that, I think I know why we don't show the inserter and the insertion point for empty blocks, that's because we replace them instead of adding new blocks when you choose a block from the inserter. I personally think it's a fine "compromise" for performance to show them even if we replace the block instead of inserting new ones. |
I find that a bit peculiar as well 😆 But it's neither here nor there, as I don't really find this to really have an effect (negative or positive) on the expectation of that behavior. |
|
Nice to see this improvement! |
* Avoid rerending the current block if the previous block change * Always show the sibling inserter * Optimize the insertion point component (#12384)
* Avoid getBlock in block-list/block * Update data docs * Fix unit tests post rebase :) * Rename props * Clarify the filter position * Avoid repeating the selector dependants * Fix small typo in the docs * Avoid rerending the current block if the previous block change (#12379) * Avoid rerending the current block if the previous block change * Always show the sibling inserter * Optimize the insertion point component (#12384) * Fix lock file * Fix linting

This PR builds on top of #11899 And it's another refactoring to the
BlockListBlockcomponent to avoid useless selectors.In my testing. Key press events are
22%faster in long posts.