skip to Main Content

I have a Wails app (React frontend – Go backend), and I am trying to do the following:

Receive button click -> Do a query on the backend -> receive response from backend -> store response on localStorage -> render page again, so it displays the updated values;

But it only works if I click the query button twice, I believe this happens because I am executing a request and then immediately updating the screen. This leaves me with the question of how to make it so the program awaits for the backend’s response before proceeding to update

function App() {

    const query = (_command) => {
        QueryBackend(_command)
        setState(state + 1)
    }

    const [state, setState] = useState(0)

    return (
        <div id="App">
            <div>
                <p> Hello, World! </p>
                <button onClick={() => query('SELECT CODSEN FROM NCMTAB')}>Query a Hello, World!</button>    
                <ul>
                    <DebugItem />
                </ul>
            </div>
        </div>
    )
};

function QueryBackend(_command) {
    Query(_command).then((res) => {
      localStorage.setItem('debugItems', res)
    })
};

function DebugItem() {
    var values = localStorage.getItem('debugItems') || `"empty": "empty"`
    return (
        <li>{values}</li>
    ) 
};


export default App

I believe I will have to structurally change my code, since I’m not very experienced in React, but I tried searching for answers everywhere (React docs, articles, etc.) and I could not for the life of me find someone that has the same situation as me.

3

Answers


  1. In your current code, the issue arises because you’re calling setState(state + 1) right after invoking QueryBackend(), without waiting for the backend query to complete. React renders the UI before the query is done, which is why you only see the updated values after clicking the button twice.

    To solve this, you need to make sure that you update the state and UI after the query response has been received. This can be done using JavaScript’s async/await or then() to wait for the backend’s response before updating the state.

    Solution with async/await:

    Make query function asynchronous: Use async/await to wait for the backend query to finish before updating the state.

    // Make query function asynchronous
    const query = async (_command) => {
        await QueryBackend(_command);  // Wait for the backend query to finish
        setState(state + 1);           // Update state after the query finishes
    };
    
    Login or Signup to reply.
  2. Updating state triggers a React component to re-render, but updating localStorage does not. And since your operation is asynchronous and not being awaited, you’re updating state immediately (and re-rendering immediately) and then updating localStorage sometime later.

    First, make your QueryBackend awaitable (basically have it return a Promise). For example:

    async function QueryBackend(_command) {
      const res = await Query(_command);
      localStorage.setItem('debugItems', res);
    };
    

    Then you can await it before updating state:

    const query = async (_command) => {
      await QueryBackend(_command);
      setState(state + 1);
    }
    

    Alternatively, if you want to stick with using .then() callbacks, then the function would need to return that Promise:

    function QueryBackend(_command) {
      return Query(_command).then((res) => {
        localStorage.setItem('debugItems', res);
      });
    };
    

    And follow it with a callback:

    const query = (_command) => {
      QueryBackend(_command).then(() => {
        setState(state + 1);
      });
    }
    

    As an aside… This is also a good time to get in the habit of terminating statements with semicolons. JavaScript is forgiving about omitting those, but the result isn’t always what the developer expects. It’s best to be explicit.

    Login or Signup to reply.
  3. I think the code needs little bit of cleanup

    • Changing the naming QueryBackend to queryBackend
      • (camelCase) javascript convention
    • Since you are not using async/await and we can do this using then/catch
    • For the queryBackend you just need to return the Query
    • You can chain the multiple thens
    • You need to change the state everytime data gets updated (button clicked)
      • The way react works, the state needs to be updated to render again
      • However, in this case you are saving in localStorage
      • So we need a counter for it.
      • To make the DebugItem render new data you need to add a key to it
        • The key over here is the counter, as it changes
    function queryBackend(_command) {
      // Returning the promise
      return Query(_command).then((res) => {
        localStorage.setItem("debugItems", res);
      });
    }
    
    function DebugItem() {
      const values = localStorage.getItem("debugItems") || `"empty": "empty"`;
      return <li>{values}</li>;
    }
    
    function App() {
      // changed the name state to counter, setCounter
      const [counter, setCounter] = useState(0);
    
      const query = (_command) => {
        // Since we are returning the promise, now we can chain another then
        queryBackend(_command).then(() => {
            setCounter(counter + 1);
        })
      };
    
      return (
        <div id="App">
          <div>
            <p> Hello, World! </p>
            <button onClick={() => query("SELECT CODSEN FROM NCMTAB")}>
              Query a Hello, World!
            </button>
            <ul>
              <DebugItem key={counter} />
            </ul>
          </div>
        </div>
      );
    }
    
    export default App;
    
    
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search