Trying to update Form state based on stored data

Hi everyone, new to JIRA App development here, and am trying to get my app to update the state of a form based on data that is stored via the Forge Storage API.

The data is storing correctly and is being retrieved but the form doesn’t update its state. Here is the code:

    const [formData, setFormData] = useState({});

    const onAppControlSubmit = async (formData) => {
        await storage.set(STORAGE_KEY_PREFIX + 'App_Control', formData);

    useEffect(async () => {
        const storedData = await storage.get(STORAGE_KEY_PREFIX + 'App_Control');
        if (storedData) {
            console.log('Information is available to display');
    }, []);

The form is a mixture of check boxes, text areas and a select drop down.

I can provide the form code if needed.

Any help would be greatly appreciated.

Hi @BrentonBulmer ,

The useEffect hook runs when one of the variables in the hook’s second argument, which is the dependency array, changes its value. However, I think you need to use the useAction because it will run when the function runs for the purpose of synchronising the state of the function with external systems such as the Forge storage service.


Thanks for the suggestion. This may sound like a simple question, but do you have an example of how I would use the useAction instead of useEffect?

I’ve tried a few things but can’t get it to execute.

According to the Forge docs this should work. I’m guessing it’s some kind of bug in their custom React environment. Some ideas to try:

  • Try setting formData in the useState hook instead of useEffect. This should work according to docs because it can take an async function. const [formData, setFormData] = useState(await storage.get(STORAGE_KEY_PREFIX + 'App_Control')); This seems like the equivalent of what you are trying to accomplish with the useEffect hook. That is, setting the intitial formData state from storage.
  • Try removing the empty dependency array in useEffect. In this case, I don’t think it’s a necessary optimization since this is rendered on the server. It’s not like client-side react, where each keystroke can trigger a possible re-render.

How do you know the item is in storage? How are you verifying this?

Hi @BrentonBulmer ,

Here is some sample code showing how useAction can be used to call an asynchronous function:

import ForgeUI, { render, Fragment, Macro, Text, useAction } from '@forge/ui';

const App = () => {

  const getRandomeNumberAsynchronously = async (): Promise<number> => {
    return new Promise((resolve, reject) => {
      const anyDelayMillis = 100;
      setTimeout(() => {
        const anyNumber = Math.random();
      }, anyDelayMillis);

  const [myRandomNumber] = useAction(value => value, async () => await getRandomeNumberAsynchronously());

  return (
      <Text>Forge useAction demo</Text>
        I retrieved the random number {myRandomNumber.toString()}

export const run = render(
    app={<App />}

In your case, you would be calling a storage operation asynchronously. Note that you can also call setMyRandomNumber() as the result of something like form handling.