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

Update the block wrapper properly when the alignment changes #22099

Closed
wants to merge 4 commits into from

Conversation

youknowriad
Copy link
Contributor

on master, when we update block alignment from not aligned to any alignment (left, right,....) or the opposite, the block toolbar disappears and don't appear only after deselecting/reselecting the block.

This PR fixes the behavior.

Testing instructions

  • Make sure that applying alignments don't hide the toolbar.

@youknowriad youknowriad added the [Type] Bug An existing feature does not function as intended label May 5, 2020
@youknowriad youknowriad requested a review from ellatrix May 5, 2020 11:37
@youknowriad youknowriad self-assigned this May 5, 2020
@@ -4,16 +4,17 @@
import classnames from 'classnames';
import { first, last, omit } from 'lodash';
import { animated } from 'react-spring/web.cjs';
import mergeRefs from 'react-merge-refs';
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was technically not needed, but I believe this is better than the "fallback" we had previously. The previous fallback wouldn't have worked with functions as refs...

Copy link
Member

Choose a reason for hiding this comment

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

Why do we need to support functions?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

we can just say we don't, but I think a React component user would expect any ref to work not just the ones created with createRef.

<Block.div ref={ node => dosomething } />

Also, this doesn't have any downside (including bundle size)

Copy link
Member

Choose a reason for hiding this comment

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

Ok, sounds good

[ clientId ]: wrapper.current,
} ) );
return () => setBlockNodes( ( nodes ) => omit( nodes, clientId ) );
}, [ isAligned ] );
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The fix here is that the block node is set now for all blocks (not just selected blocks) and is refreshed only when the "isAligned" changes because that's the only thing that can affect it here.

Copy link
Member

Choose a reason for hiding this comment

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

Why do we need all block nodes for alignment?
I suspect that this will impact loading performance because this blocks layout. Perhaps we can use useEffect instead, but I recall having some e2e test problems with that.

Copy link
Member

Choose a reason for hiding this comment

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

Generally, I do think storing all reference would be useful, especially since it could replace the "ugly" querying of block nodes in other places. Not sure why we need it in this PR though.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I checked the performance impact and I don't think there's one. That said I'd appreciate confirmation since there are fluctuations.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Why do we need all block nodes for alignment?

Assigning or unassigning an alignment rerenders the block entirely and generates a new "node" for this block. (because there's an extra wrapper needed)

Copy link
Member

Choose a reason for hiding this comment

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

Oh, does a component not remount when that happens?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

no, it doesn't, the element change but the component is still the same.

Copy link
Member

Choose a reason for hiding this comment

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

Hm, I wonder if we should update the list on every rerender, if the node is not the same.

Copy link
Member

Choose a reason for hiding this comment

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

Or maybe we should store the ref object rather than the element itself?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

the node only changes on mount/unmout and on alignment set/unset because the element tree changes at that moment. I tried using a "function as ref" because that's that function is called at the exact moment the ref changes but it doesn't work because that function is called at "render" time by React causing infinite loops and weird breakage.

What I have now is basically the same thing as "function as ref" but called as an effect (once the render is finished).

@github-actions
Copy link

github-actions bot commented May 5, 2020

Size Change: +14 B (0%)

Total Size: 822 kB

Filename Size Change
build/block-editor/index.js 101 kB +14 B (0%)
ℹ️ View Unchanged
Filename Size Change
build/a11y/index.js 1.02 kB 0 B
build/annotations/index.js 3.62 kB 0 B
build/api-fetch/index.js 4.08 kB 0 B
build/autop/index.js 2.82 kB 0 B
build/blob/index.js 620 B 0 B
build/block-directory/index.js 6.6 kB 0 B
build/block-directory/style-rtl.css 760 B 0 B
build/block-directory/style.css 761 B 0 B
build/block-editor/style-rtl.css 10.2 kB 0 B
build/block-editor/style.css 10.2 kB 0 B
build/block-library/editor-rtl.css 7.08 kB 0 B
build/block-library/editor.css 7.08 kB 0 B
build/block-library/index.js 115 kB 0 B
build/block-library/style-rtl.css 7.24 kB 0 B
build/block-library/style.css 7.25 kB 0 B
build/block-library/theme-rtl.css 683 B 0 B
build/block-library/theme.css 685 B 0 B
build/block-serialization-default-parser/index.js 1.88 kB 0 B
build/block-serialization-spec-parser/index.js 3.1 kB 0 B
build/blocks/index.js 48.1 kB 0 B
build/components/index.js 179 kB 0 B
build/components/style-rtl.css 16.9 kB 0 B
build/components/style.css 16.9 kB 0 B
build/compose/index.js 6.66 kB 0 B
build/core-data/index.js 11.4 kB 0 B
build/data-controls/index.js 1.29 kB 0 B
build/data/index.js 8.44 kB 0 B
build/date/index.js 5.47 kB 0 B
build/deprecated/index.js 772 B 0 B
build/dom-ready/index.js 568 B 0 B
build/dom/index.js 3.1 kB 0 B
build/edit-navigation/index.js 4.07 kB 0 B
build/edit-navigation/style-rtl.css 485 B 0 B
build/edit-navigation/style.css 485 B 0 B
build/edit-post/index.js 28.1 kB 0 B
build/edit-post/style-rtl.css 12.2 kB 0 B
build/edit-post/style.css 12.2 kB 0 B
build/edit-site/index.js 12.3 kB 0 B
build/edit-site/style-rtl.css 5.19 kB 0 B
build/edit-site/style.css 5.2 kB 0 B
build/edit-widgets/index.js 8.37 kB 0 B
build/edit-widgets/style-rtl.css 4.68 kB 0 B
build/edit-widgets/style.css 4.68 kB 0 B
build/editor/editor-styles-rtl.css 428 B 0 B
build/editor/editor-styles.css 431 B 0 B
build/editor/index.js 44.3 kB 0 B
build/editor/style-rtl.css 5.07 kB 0 B
build/editor/style.css 5.08 kB 0 B
build/element/index.js 4.65 kB 0 B
build/escape-html/index.js 734 B 0 B
build/format-library/index.js 7.63 kB 0 B
build/format-library/style-rtl.css 502 B 0 B
build/format-library/style.css 502 B 0 B
build/hooks/index.js 2.13 kB 0 B
build/html-entities/index.js 622 B 0 B
build/i18n/index.js 3.56 kB 0 B
build/is-shallow-equal/index.js 710 B 0 B
build/keyboard-shortcuts/index.js 2.51 kB 0 B
build/keycodes/index.js 1.94 kB 0 B
build/list-reusable-blocks/index.js 3.13 kB 0 B
build/list-reusable-blocks/style-rtl.css 226 B 0 B
build/list-reusable-blocks/style.css 226 B 0 B
build/media-utils/index.js 5.29 kB 0 B
build/notices/index.js 1.79 kB 0 B
build/nux/index.js 3.4 kB 0 B
build/nux/style-rtl.css 616 B 0 B
build/nux/style.css 613 B 0 B
build/plugins/index.js 2.56 kB 0 B
build/primitives/index.js 1.5 kB 0 B
build/priority-queue/index.js 789 B 0 B
build/redux-routine/index.js 2.85 kB 0 B
build/rich-text/index.js 14.8 kB 0 B
build/server-side-render/index.js 2.67 kB 0 B
build/shortcode/index.js 1.7 kB 0 B
build/token-list/index.js 1.28 kB 0 B
build/url/index.js 4.02 kB 0 B
build/viewport/index.js 1.84 kB 0 B
build/warning/index.js 1.14 kB 0 B
build/wordcount/index.js 1.18 kB 0 B

compressed-size-action

@youknowriad
Copy link
Contributor Author

There's an e2e test failure, I tried CPU debounce but I'm not able to reproduce locally :(

@youknowriad youknowriad force-pushed the fix/alignment-wrapper branch from c66b0bd to 65a43c4 Compare May 5, 2020 13:03
@ellatrix
Copy link
Member

ellatrix commented May 5, 2020

I see a big change in loading time. (make sure you run npm install or the build will fail and you'll test the previous build.)

master:

Loading Time:
Average time to load: 5667ms
Average time to DOM content load: 5639ms

pr:

Loading Time:
Average time to load: 6539ms
Average time to DOM content load: 6509ms

@ellatrix
Copy link
Member

ellatrix commented May 5, 2020

Could we leave out setting all block nodes for now? I don't think it's needed for this PR?

@ellatrix ellatrix mentioned this pull request May 5, 2020
6 tasks
@ellatrix
Copy link
Member

ellatrix commented May 5, 2020

Ok, I created #22108, which removes useLayoutEffect and should probably fix the performance issue.

@ellatrix ellatrix force-pushed the fix/alignment-wrapper branch from 65a43c4 to 07b3b24 Compare May 5, 2020 19:00
@ellatrix
Copy link
Member

ellatrix commented May 5, 2020

So 😅 it is slower now... probably because useEffect will cause re-renders and useLayoutEffect will not. Maybe we can move this part to a separate PR? (Setting all block nodes.) Not immediately sure what the solution is. We could request an idle callback and set the nodes then, but I'm not sure if that will be much better. Ideally, all nodes should be set at the same time and only cause 1 re-render. Or we need to avoid using state entirely.

@ellatrix
Copy link
Member

ellatrix commented May 5, 2020

@youknowriad Would you be ok with splitting out the fix (#22117)?

@youknowriad
Copy link
Contributor Author

@ellatrix It still seems like an optimization tailored for a particular use-case (toolbar) while the context is more generic "block nodes" but I agree with landing it first.

@youknowriad
Copy link
Contributor Author

About the performance impact, even if the context is updated more often, It seems that it shouldn't necessarily result in a lot more rerenders since, in theory, only the "consumer" (block toolbar) rerenders. There might be an optimization to be done elsewhere.

@ellatrix
Copy link
Member

ellatrix commented May 6, 2020

So what we need:

  • Dispatching that doesn't re-render the block wrapper. In other words, the block wrapper should not depend on the block nodes state.
  • A state selector that doesn't cause a re-render when other block nodes are added or updated.

This sounds a lot like useSelect and useDispatch. I originally used the block-editor store for the block nodes, but we didn't see it as a good fit. Maybe a small internal store?

@youknowriad
Copy link
Contributor Author

if we were to use a store, it seems "block-editor" is the right one but I'm not sure why the context approach can't work similarly? maybe we just need to "memo" the block nodes provider's children or something like that.

@ellatrix
Copy link
Member

ellatrix commented May 6, 2020

Yeah, it should be possible to do. I'm not sure if setState is memoized, I'll need to check.

@ellatrix
Copy link
Member

ellatrix commented May 6, 2020

Ok, just quickly confirmed with a test that it is: https://codepen.io/iseulde/pen/YzyewKB

@ellatrix ellatrix force-pushed the fix/alignment-wrapper branch from 07b3b24 to 6060366 Compare May 6, 2020 11:07
@ellatrix
Copy link
Member

ellatrix commented May 6, 2020

Found a fix I think. We could wait to set the block node in the first idle period. That's ok, dependent on the node already have to wait for the information since it's not provided synchronically. We could potentially only delay it on the first render.

useEffect( () => {
if ( isSelected || isFirstMultiSelected || isLastMultiSelected ) {
const node = wrapper.current;
const id = window.requestIdleCallback( () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

this function is not available on IE, we polyfill it somehow on @wordpress/priority-queue.

Copy link
Member

Choose a reason for hiding this comment

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

Oh yeah, forgot about that

@ellatrix
Copy link
Member

ellatrix commented May 6, 2020

As expected, this triggers some No node found for selector e2e test error, which can be easily adjusted to wait for the selector. The rendering of the block toolbar is now a tiny bit delayed, but that's not noticeable by the user.

@youknowriad
Copy link
Contributor Author

the only potential downside is that you need to know that it's async on the consuming level (I mean don't assume that it's always available)

@ellatrix
Copy link
Member

ellatrix commented May 6, 2020

You cannot currently make that assumption either, because it's only set in a useEffect callback. You cannot even assume it's there during a useEffect callback in the consumer's component, because you don't know which one will be rendered first. In addition to that, setState is async as well.

@ellatrix
Copy link
Member

ellatrix commented May 6, 2020

There seem to be a few e2e test failure which are definitely caused by this code change (don't see them in master).

@youknowriad
Copy link
Contributor Author

I think we already fixed this differently.

@youknowriad youknowriad deleted the fix/alignment-wrapper branch June 22, 2020 09:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Type] Bug An existing feature does not function as intended
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants