volto icon indicating copy to clipboard operation
volto copied to clipboard

Cannot use useSelector in the "restricted" function of a Block

Open wesleybl opened this issue 1 year ago • 2 comments

Describe the bug I'm trying to make only a user with a Manager role add a certain block. I tried to restrict the block with the restricted function:

https://6.docs.plone.org/volto/development/how-to-restrict-blocks.html

To get the user I used the userSelector inside the restricted function. So when I click on a Text block when editing content, I get the error:

Uncaught TypeError: Cannot read properties of undefined (reading 'length')
    at areHookInputsEqual (react-dom.development.js:16249:1)
    at updateEffectImpl (react-dom.development.js:17078:1)
    at updateEffect (react-dom.development.js:17098:1)
    at Object.useEffect (react-dom.development.js:17862:1)
    at Object.useEffect (react.development.js:1634:1)
    at useIsMounted (SlashMenu.jsx:24:1)
    at PersistentSlashMenu (SlashMenu.jsx:161:1)
    at renderWithHooks (react-dom.development.js:16305:1)
    at updateFunctionComponent (react-dom.development.js:19588:1)
    at beginWork (react-dom.development.js:21601:1)
    at HTMLUnknownElement.callCallback (react-dom.development.js:4164:1)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:4213:1)
    at invokeGuardedCallback (react-dom.development.js:4277:1)
    at beginWork$1 (react-dom.development.js:27451:1)
    at performUnitOfWork (react-dom.development.js:26557:1)
    at workLoopSync (react-dom.development.js:26466:1)
    at renderRootSync (react-dom.development.js:26434:1)
    at performSyncWorkOnRoot (react-dom.development.js:26085:1)
    at flushSyncCallbacks (react-dom.development.js:12042:1)
    at react-dom.development.js:25651:1

Analyzing the error, I found that it occurs because the useSelector call occurs inside a useMemo, which is not recommended. The useMemo in question is this one:

https://github.com/plone/volto/blob/0edcd8083afee783a6463a14b18d0f712cf7c25b/packages/volto-slate/src/blocks/Text/SlashMenu.jsx#L119

And the call to the restricted function, which calls useSelector occurs here:

https://github.com/plone/volto/blob/0edcd8083afee783a6463a14b18d0f712cf7c25b/packages/volto-slate/src/blocks/Text/SlashMenu.jsx#L125

I believe it is not a good idea to remove useMemo. So what can be done? Is there any other way to restrict adding a block only to Manager?

This is related to #5137

To Reproduce Steps to reproduce the behavior:

  1. To make reproduction easier, we will do it in a block of Volto itself.
  2. In the file:

https://github.com/plone/volto/blob/main/packages/volto/src/config/Blocks.jsx

import useSelector:

import { useSelector } from 'react-redux';
  1. Replace the restricted in the HTML block:

https://github.com/plone/volto/blob/a4965580f3d443d1b102344d2fcb9d8ed1728ec7/packages/volto/src/config/Blocks.jsx#L386

to:

    restricted: () => {
      useSelector((state) => state.users?.user);
    },
  1. Add a Document and click on an empty Text block.

Expected behavior There should be no error when clicking on a Text block.

Software (please complete the following information):

Volto 18.0.0-alpha.42 Plone 6.0.11 plone.restapi 9.7.0 CMF 3.5 Zope 5.9 Python 3.11.9 (main, May 14 2024, 08:32:26) [GCC 10.2.1 20210110] PIL 9.5.0 (Pillow)

wesleybl avatar Aug 21 '24 15:08 wesleybl

The option I see would be to get the user in the PersistentSlashMenu component itself, and pass it to the restricted function. Would that be acceptable?

wesleybl avatar Aug 21 '24 16:08 wesleybl

The option I see would be to get the user in the PersistentSlashMenu component itself, and pass it to the restricted function. Would that be acceptable?

@wesleybl That sounds like a pretty good solution to me. It's backwards compatible with existing implementations of restricted and sounds generally useful.

davisagli avatar Aug 24 '24 04:08 davisagli