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

useToolsPanel: calculate menuItems in layout effect to avoid painting intermediate state #65494

Open
wants to merge 3 commits into
base: trunk
Choose a base branch
from

Conversation

jsnajdr
Copy link
Member

@jsnajdr jsnajdr commented Sep 19, 2024

Fixes a visual glitch when displaying a tools panel in style editor. Go to global styles editor for individual blocks and in the list of blocks, click on Paragraph. At first, toolbar items with no content will appear:

tools-panel-empty

only after a moment the full UI is rendered:

tools-panel-final

The cause of this is how the state of the panel is updated, calculating derived state in a cascade of effects:

const [ a, setA ] = useState();
const [ b, setB ] = useState();
const [ c, setC ] = useState();

useEffect( () => {
  setB( ... );
}, [ a ] );

useEffect( () => {
  setC( ... );
}, [ b ] );

This needs several rerenders and paints until the final state is achieved.

In our case, the state involved is the menuItems object and the isShown value derived from it.

This PR fixes that by using layout effect, so that all the updates happen without an intermediate paint that reveals the non-final UI state. See also #56536 which applied exactly the same fix at another place.

Only the first commit is needed to actually fix the glitch. The rest is using useLayoutEffect also for other state-updating effects in the same file. And removing some unneeded dependencies from useCallback and useEffect calls, namely state setters.

The next step could be a bigger refactoring to make the state updates more streamlined. We don't need effects: they only trigger rerenders. We could use useMemo or, for state updates that need to see the previous state, a useReducer.

@jsnajdr jsnajdr added [Type] Bug An existing feature does not function as intended [Package] Components /packages/components labels Sep 19, 2024
@jsnajdr jsnajdr self-assigned this Sep 19, 2024
@ciampo
Copy link
Contributor

ciampo commented Sep 19, 2024

The changes make sense to me, although they seem to cause errors in unit tests. I guess we'd need to understand if we can improve the performance and timing of the updates, while keeping the expected behavior.

Only the first commit is needed to actually fix the glitch. The rest is using useLayoutEffect also for other state-updating effects in the same file. And removing some unneeded dependencies from useCallback and useEffect calls, namely state setters.

Maybe removing the second commit would fix the unit tests?

Copy link

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: jsnajdr <[email protected]>
Co-authored-by: ciampo <[email protected]>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@jsnajdr
Copy link
Member Author

jsnajdr commented Sep 19, 2024

they seem to cause errors in unit tests.

I fixed that by using layout effects also in ToolsPanelItem, so that the order of execution stays the same. The tool panels are a maze of fragile mutual updates, the code deserves a thorough review and refactor.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Package] Components /packages/components [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