Error: dispatch must be called inside of an event handler or within the function arguments of useAction, useState or useContentProperty

Hi!

I’m running into some redux error, which I’m unable to fix nor understand so far.

I’m trying to create a forge ui kit app that inventories all macros used on a Confluence page, here’s part of the code

// Get page body by contentId and find all macros used on the page and return them in an array of objects
const getPageMacros = async (contentId) => {
  const page = await getPage(contentId);
  console.log(page);
  const bodyStorageFormat = page.body.storage.value;
  console.log(bodyStorageFormat);
  const pageMacrosStorageFormat = findPageMacros(bodyStorageFormat);
  console.log(pageMacrosStorageFormat);
  let pageMacros = [];
  if (pageMacrosStorageFormat) {
    for (let i = 0; i < pageMacrosStorageFormat.length; i++) {
      pageMacros[i] = findPageMacroProperties(pageMacrosStorageFormat[i]);
    }
  }
  console.log(pageMacros);
  return pageMacros;
}

const App = () => {
  const [isOpen, setOpen] = useState(true);
  const context = useProductContext();
  const [pageMacrosObjects] = useState(async () => await getPageMacros(context.contentId));
  if (pageMacrosObjects.length == 0) {  doSomething();  }
  if (!isOpen) {
    return null;
  }
  return (
    <ModalDialog header="Macros" width="medium" onClose={() => setOpen(false)}>
      <Text>There {pageMacrosObjects.length == 1 ? `is ${pageMacrosObjects.length} macro` : `are ${pageMacrosObjects.length} macros`} on this page</Text>
     </ModalDialog>
  );
};

export const run = render(
  <ContentAction><App/></ContentAction>
);

The thing is, everything works fine as long as I leave out the the following part:
if (pageMacrosObjects.length == 0) { doSomething(); }

But if I leave it in, I’m getting the following error:

ERROR  bb93749ce9e03089  dispatch must be called inside of an event handler or within the function arguments of useAction, useState or useContentProperty
Error: dispatch must be called inside of an event handler or within the function arguments of useAction, useState or useContentProperty
    at ReconcilerState.enqueueSideEffectIfEnabled (webpack://confluence-content-action-ui-kit/node_modules/@forge/ui/out/reconcilerState.js:67)
    at /tmp/tunnel7KDuu9yhIhcf7/index.js:3202:39
    at Object.App [as type] (webpack://confluence-content-action-ui-kit/src/index.jsx:114)
    at /tmp/tunnel7KDuu9yhIhcf7/index.js:3562:36
    at async asyncMap (webpack://confluence-content-action-ui-kit/node_modules/@forge/ui/out/reconcile.js:13)
    at async /tmp/tunnel7KDuu9yhIhcf7/index.js:3518:29
    at async /tmp/tunnel7KDuu9yhIhcf7/index.js:2912:31

This error only shows up when the getPageMacros returns an empty array [], if macros are found, there is no problem and everything still works fine.

So my questions are:

  • Why can I use the pageMacrosObjects.length without problems in the return statement of the App, even if there are no macros found, but not before the return statement?
  • Why is it giving this error and how do I fix this? I would like to validate the getPageMacros return value before the return statement in App.

Some help/insight is really appreciated :smiley: Thanks in advance!

Rick

Have you tried setting doSomething to a function that does nothing? const doSomething= ()=>{} If you do that and you don’t get the error, you know whatever is happening inside doSomething is your problem. From your description, it sounds like that is the problem. It’s the only difference in the code path in the failure scenario in your description.

1 Like

@ryan That actually helped me fix the issue I was having, thank you very much!

Strange thing is though, that the doSomething() function should be pretty straightforward functionality:

const [feedbackMessage, setFeedbackMessage] = useState(undefined);

  if (!pageMacrosObjects.length) {
    setFeedbackMessage("There were no macros found on this page");
  }

so, setFeedbackMessage() is throwing the error above, which i still don’t understand but I was able to workaround it.

Conceptually, I see what you are doing wrong. If there are no macros on the page, you are basically mutating the React state in an infinite loop.

When the state is changed, React will try to re-render the component and therefore call your setFeedbackMessage again and therefore try to re-render the component and keep repeating that cycle. You would probably get an error with normal client-side React as well.