1

I've been building a Gutenberg Block that sends a GET request to the Woocommerce REST API.

It is composed of a text input that receives an ID of a product and fetches it to the Woocommerce REST API. It also has a button that fires a function to fetch the product with the API.

The issue with the GET Requests

The fetching works well, but it keeps sending multiple GET requests, even when I do not click the button to fire the function. Simply clicking the input sends multiple requests when I only need one everytime I change the ID and click the button.

I'm importing WooCommerceRestApi from "@woocommerce/woocommerce-rest-api" and use Node.js.

The code

This is the first part of the edit function:

const edit = ({ attributes, setAttributes }) => {

    const blockProps = useBlockProps();

    // Wrapped the WooCommerce.get() function in a function named `fetchingId` to call it with a button
    const fetchingId = async (id) => {
      // The WooCoommerce.get() function
      const response = await WooCommerce.get(`products/${id}`)
        .then((response) => {
          console.log(response.data);
          setAttributes({ price: response.data.price });
          setAttributes({ name: response.data.name });
        })
        .catch((error) => {
          console.log(error.response.data);
          setAttributes({ price: '' });
          setAttributes({ name: '' });
        });
    }

    ...

  }

This is another part of the function: an input that updates the Product ID that is used for the GET request and a button linked to the fetchingId() function.

return <div {...blockProps}>
    <div class="wrapper-input" >
        <TextControl
          label={__('ID du Produit', 'ingredient-unique')}
          value={attributes.id}
          onChange={(val) => setAttributes({ id: val })}
          className='control-id'
        />
    </div>
    <button onclick={fetchingId(attributes.id)}>Chercher le produit</button>

    ...
</div>;
1
  • 1
    onclick={fetchingId(attributes.id)} - did you forget the () => as in onclick={() => fetchingId(attributes.id)} ? Because you should actually pass a function and not the result of a function call. Otherwise, that would result in the fetchingId() being called everytime the component is rendered.. See this FAQ on the React website, but that "pass a function" is not just in React - even in native JS, that's indeed how it works.
    – Sally CJ
    Feb 10 at 13:12
2

You're not supposed to make HTTP requests etc directly inside React components, you need to use useEffect.

A react component needs to run as quickly as possible, even if work that needs doing isn't finished yet. By using useEffect you tell React to run a separate function after rendering is complete, giving you the opportunity to make requests and for your component to have "effects". React also lets you pass a list of dependencies that it will watch, and if they change the effect re-runs.

What you have however, is a side effect that runs directly in the component. As a result, whenever the component is re-rendered, your code makes a HTTP fetch, which aside from this problem is also going to significantly slow down the editor. A re-render could happen at any time by design. A window resize, stray dom event, state changes in parent components, etc

If it helps, a common way to show this is with something like this:

const { isLoading, setLoading } = useState( false );

This way you can call setLoading( false ) at the end of your useEffect function, and then display a loading message in the component

This is another part of the function: an input that updates the Product ID that is used for the GET request and a button linked to the fetchingId() function.

You cannot rely on those values being present here. You can trigger a function via an event, and that function can have side effects such as HTTP requests, but you can't do those things directly in the component. Rendering the component should render the component, and only render the component, everything else goes in lifecycle hooks/functions

3
  • Thank you for the detailed explanation ! I've been trying to use useEffect but I cannot find where to put it. Should it be after the registerBlockType function ? Should I import useEffect from react ?
    – JAGENI Nat
    Feb 10 at 12:58
  • 1
    useEffect is a react hook, it isn't a WordPress thing, import it like this: import { useEffect } from '@wordpress/element'; otherwise it works the same way as in any other React component.
    – Tom J Nowell
    Feb 10 at 13:20
  • 1
    Remember, your edit component is not a block. A block has/contains an edit component, aka a React component. It's just React, look up any react tutorial on useEffect, useState, or react hooks
    – Tom J Nowell
    Feb 10 at 13:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.