Search code examples
reactjsreact-hooksuse-effect

Issue with useEffect dependency array


My project is fetching data from API using the useState and useEffect hooks. The request is working and rendering properly at my local host, but I'm getting the following warning:

React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array.

The warning is preventing me from deploying my project. I've tried both adding 'fetchData' to array as well as removing the array entirely. Both result in errors.

Adding 'fetchData' to array results in another warning:

The 'fetchData' function makes the dependencies of useEffect Hook (at line 31) change on every render. Move it inside the useEffect callback. Alternatively, wrap the definition of 'fetchData' in its own useCallback() Hook.

Removing the array results in this console error:

Access to fetch at from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

All I need is for the state to render once on load. Here is my code:

export function Product({Inventory_ID, Cost}) {
  const [ data, setData ] = useState("");
  const [ centers, setCenters ] = useState([]);

  const fetchData = async () => {
    try {
      const response = await fetch(`${API_ENDPOINT}${Inventory_ID}`, REQUEST_OPTIONS);
      const data = await response.json();
      setData(data);

      const centers = await data.fulfillable_quantity_by_fulfillment_center;
      setCenters(centers);
    } catch (e) {
      console.error(e);
    }
  }

  useEffect(() => {
    fetchData();
  }, []);

  // Currency formatter
  const formatter = new Intl.NumberFormat('en-US', {
    style: 'currency',
    currency: 'USD',
    maximumFractionDigits: 0,
  })

  return (
    <div className="card">
      <h2>{data.name}</h2>
      <h3>Total Onhand Quantity</h3>
      <p>{data.total_onhand_quantity}</p>
      <h3>Total Onhand Value</h3>
      <p>{formatter.format(data.total_onhand_quantity * Cost)}</p>
      <h3>Quantity by Fulfillment Center</h3>
      {centers.map((center) => {
        return (
          <div>
          <h4 key={center.name}>{center.name}</h4>
          <p key={center.onhand_quantity}>{`Onhand Quantity: ${center.onhand_quantity}`}</p>
          <p key={center.awaiting_quantity}>{`Awaiting Quantity: ${center.awaiting_quantity}`}</p>
          </div>

        );
      })}
      <div>

      </div>
    </div>
  )
}

Any idea where I'm going wrong here and how to resolve?

Thanks so much.


Solution

  • Not sure what you did before to get those errors, but the code you have now looks correct. Your problem now is that whatever API_ENDPOINT is doesn't have CORS enabled. What that means is that the server doesn't have your domain whitelisted for "you can make a request" so when it hits your browser your browser is "protecting" you by invalidating your fetch call.

    In order to fix that you need to enable CORS on your API server and you need to whitelist the domain your UI is running on. Or you can create your own API and pull the data from there, then you can fetch from their API server side so that your browser doesn't CORS reject you.

    Side note, I'm not sure if I'm correct about it but this might give you an issue:

    setData(data);
    const centers = await data.fulfillable_quantity_by_fulfillment_center;
    setCenters(centers);
    

    Or at the very least it'll just be pretty unoptimized. React will set the state, rerender the component, while wathing for the second fetch call to finish, then it will set the state again and rerender again.

    Instead make all your fetch calls and after that set all the states so that react can render the component with a batch update.