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

useBlockProps rerender block multiple times #56664

Open
wpsoul opened this issue Nov 30, 2023 · 13 comments
Open

useBlockProps rerender block multiple times #56664

wpsoul opened this issue Nov 30, 2023 · 13 comments
Labels
Needs Technical Feedback Needs testing from a developer perspective. [Type] Question Questions about the design or development of the editor.

Comments

@wpsoul
Copy link

wpsoul commented Nov 30, 2023

Description

I implemented API 3 to blocks and see big degradation of performance. After some research, I found that useBlockProps rerender block multiple times. Also it rerenders blocks when it's in View.

Step-by-step reproduction instructions

  1. Add console.log in edit function
  2. Add useBlockProps and check console. I see 4 times execution, also when I scroll down/up, I see execution when block is in view
  3. Now, remove useBlockProps. Console executed 1 time only

Screenshots, screen recording, code snippet

Monosnap.screencast.2023-11-30.06-07-33.mp4

Environment info

6.4 Wordpress

Please confirm that you have searched existing issues in the repo.

Yes

Please confirm that you have tested with all plugins deactivated except Gutenberg.

Yes

@wpsoul wpsoul added the [Type] Bug An existing feature does not function as intended label Nov 30, 2023
@wpsoul
Copy link
Author

wpsoul commented Nov 30, 2023

After more testing, I simplified block and it renders 2 times but still rerender on view even if edit function has only "div" and nothing more. How to prevent double render and rerender on view?

@Mamaduka Mamaduka added Needs Technical Feedback Needs testing from a developer perspective. [Type] Question Questions about the design or development of the editor. and removed [Type] Bug An existing feature does not function as intended labels Nov 30, 2023
@Mamaduka
Copy link
Member

This isn't a bug; re-rendering in React is usually fast unless a component performs some heavy/slow computations.

The useBlockProps grabs required information for a block and keeps track of various block states, which can trigger a re-render so that the block displays the latest data.

@wpsoul
Copy link
Author

wpsoul commented Nov 30, 2023

@Mamaduka I can't agree with this. There is no reason to make double render the same block on init (I don't see this without useBlockProps) and there is no reason to rerender block when it's in view because block doesn't change state on this trigger. Imagine that page can have hundreds of blocks and just scrolling will make rerender them with slow performance. I will continue tests to find where this strange re render is triggered

@wpsoul
Copy link
Author

wpsoul commented Dec 3, 2023

So, is there any solution to remove rerender on View? I think it's using Intersection Observer but I don't see reason for blocks to rerender when they are in view

@Mamaduka
Copy link
Member

Mamaduka commented Dec 7, 2023

cc @WordPress/gutenberg-core

@t-hamano
Copy link
Contributor

t-hamano commented Dec 7, 2023

There is useIntersectionObserver in the ueBlockProps hook that seems to cause re-rendering when scrolled. However, reading #30995, it looks like it was introduced intentionally to solve some problem.

@youknowriad
Copy link
Contributor

the intersection observer allows us to enable "async mode" for the blocks that are out of view. Meaning the editor will be more responsive because these out of view blocks will have "async mode" active.

So if this re-rendering is inevitable because of the intersection observer (we need to confirm that this assumption), then it's fine because the gains from async mode are huge compared to a single re-rendering.

@wpsoul
Copy link
Author

wpsoul commented Dec 8, 2023

@youknowriad this can have a sense if observer is working only once. What happens now on loading

  1. All blocks are rendering (even those which are not in view)
  2. User scroll down, they are rerendering. Why? Blocks already rendered, they didn't change states, no need to rerender them again
  3. When user scroll down and scroll up, blocks are again rerendered.

So, Observer doesn't solve anything, it's just adding new rerender on top of existed

@youknowriad
Copy link
Contributor

@wpsoul I can guarantee that you're wrong. Try disabling the observer and type in the editor and you'll see what I'm talking about. There might be a couple initial re-rendering happening and we could try to debug that to see what we can do there. But saying that the observer is not doing anything is not true.

Typing metric right now is 30ms in codevitals.run without it, it could be x10 slower

@wpsoul
Copy link
Author

wpsoul commented Dec 8, 2023

@youknowriad I already showed this on video. Old API doesn't re rerender blocks. it's just rendering 1 time as it should. Can you explain me how constant multiple rerendering of block can improve performance? What is the reason of rerender blocks if they are already rendered and they are not changed?

@youknowriad
Copy link
Contributor

youknowriad commented Dec 8, 2023

When a block goes out of view it becomes "async" meaning that if you make a change to another block (like typing in another block), your block will run its selectors (any useSelect call and a lot of hooks and things like that) asynchronously, when the browser is idle.

So if you have a post with 1000 blocks and 980 are out of view, these blocks will be async and the visible ones will be synchronous (call selectors to check if they should update on each change).

The downside, is that to do that, we need to switch the "isAsync" for all blocks when they go "out of view" or "in view". This is a very small price to pay compared to rendering 1000 blocks synchronously causing huge lags for the user when typing.

@wpsoul
Copy link
Author

wpsoul commented Dec 9, 2023

if so, then why my blocks on API 1 are fine to load and browse and on API 3 - very laggy? interesting, that this is mostly related to situations when I have many inner blocks. For example, if I add 300 blocks on page - it's fine, a bit slow but not critical. But when I start to add groups, rows and many nested elements, this is so slow, almost not usable. And inside FSE it's even worse, my hosting usually goes down when I try to save FSE page with many elements.

@talldan
Copy link
Contributor

talldan commented Dec 11, 2023

I already showed this on video.

What you're doing on the video doesn't seem quite right as you're just adding/removing useBlockProps without changing the api version. If you change the api version of your block back to 1 and remove useBlockProps then I'd expect it to also re-render when going out of view.

The code here shows how the compatibility works:

} else if ( blockType?.apiVersion > 1 ) {
block = blockEdit;
} else {
block = <Block { ...wrapperProps }>{ blockEdit }</Block>;
}

When using api version 1, the else branch will wrap your block for you and call useBlockProps so it'll work the same:

function Block( { children, isHtml, ...props } ) {
return (
<div { ...useBlockProps( props, { __unstableIsHtml: isHtml } ) }>
{ children }
</div>
);
}

A v2/3 block without useBlockProps may not work correctly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs Technical Feedback Needs testing from a developer perspective. [Type] Question Questions about the design or development of the editor.
Projects
None yet
Development

No branches or pull requests

5 participants