How to Fix React App Production Build Shows JS Errors

When you’re building a React application using create-react-app and deploying it to production, everything may seem perfect during development, but once you run npm run build, you might encounter unexpected issues. One of the common JavaScript errors that developers face in production builds is:

Error Code:

: "e.forEach is not a function"
me InboundTab.js:27
me InboundTab.js:36
React 7
unstable_runWithPriority scheduler.production.min.js:270
React 5
getArrivals ArrivalState.js:39

This error can seem a bit cryptic, especially if everything was working perfectly in development. In this blog post, I’ll walk you through the root cause of this error, how to fix it, and share additional tips for preventing similar issues in the future.

Understanding the Problem

The error message TypeError: "e.forEach is not a function" typically happens when the forEach method is called on a variable that isn’t an array or is undefined. This may not show up in development, where the code might be more lenient with unhandled edge cases, but it can crop up when the app is built for production.

In this case, you’re calling forEach on the arrivals variable, which is expected to be an array. However, when the application is running in production, the data that’s being fetched may not be an array (it could be null, undefined, or an object), causing the error.

Code Explanation

Let’s dive into the code that likely caused this error.

Here’s an example of the problematic code in InboundTab.js:

React, { useEffect, useState } from 'react';

const InboundTab = () => {
const [arrivals, setArrivals] = useState([]);

useEffect(() => {
fetch('/api/arrivals')
.then(response => response.json())
.then(data => setArrivals(data)); // Assuming the data is an array
}, []);

const renderArrivals = () => {
// Assuming arrivals should be an array, but it could be undefined or an object
arrivals.forEach((arrival, index) => {
console.log(index, arrival);
});
};

return (
<div>
<button onClick={renderArrivals}>Show Arrivals</button>
</div>
);
};

In this code, you’re fetching data from an API endpoint (/api/arrivals) and expecting the returned data to be an array. If the fetched data isn’t an array (for example, if it’s an object, null, or something unexpected), calling forEach on it will throw the error "e.forEach is not a function".

Error Explanation

The TypeError: "e.forEach is not a function" occurs because the arrivals variable is being treated as an array, but it’s not one. There are several reasons why this might happen:

  • API response issues: The API might not return an array but an object, null, or another data type.
  • Incorrect initial state: Although the initial state of arrivals is set to an empty array, the fetched data might not be an array when it arrives.
  • Data structure issues: The expected data might not be structured correctly, or there might be a delay in data fetching, causing an undefined or incorrect response.

How to Fix It

To fix this issue, you should check whether the data fetched from the API is actually an array before attempting to call forEach. This can be done using Array.isArray().

Here’s an updated version of the code that includes this check:

React, { useEffect, useState } from 'react';

const InboundTab = () => {
const [arrivals, setArrivals] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);

useEffect(() => {
fetch('/api/arrivals')
.then(response => response.json())
.then(data => {
// Check if the fetched data is an array before setting the state
if (Array.isArray(data)) {
setArrivals(data);
} else {
setError('Expected an array but got something else');
console.error('Expected an array but got:', data);
}
setLoading(false);
})
.catch(error => {
setError('Failed to fetch data');
console.error('Failed to fetch data:', error);
setLoading(false);
});
}, []);

const renderArrivals = () => {
if (loading) {
return <div>Loading...</div>;
}

if (error) {
return <div>Error: {error}</div>;
}

if (!Array.isArray(arrivals)) {
console.error('arrivals is not an array');
return;
}

return arrivals.map((arrival, index) => (
<div key={index}>
{arrival.name} - {arrival.time}
</div>
));
};

return (
<div>
<button onClick={renderArrivals}>Show Arrivals</button>
{renderArrivals()}
</div>
);
};

In this solution:

  • Check for arrays: Before setting the state with the fetched data, we use Array.isArray(data) to ensure it’s an array.
  • Error state: If the data isn’t an array, we set an error state to display a message and log the error for debugging.
  • Loading state: We added a loading state to display a loading message while waiting for the API response.
  • Error handling: A fallback error message is shown if something goes wrong during the data fetch.

Extra Practice and Functionality to Add

  1. Error Boundaries: To further improve your error handling, consider using React’s Error Boundaries. These are special components that catch JavaScript errors anywhere in their child component tree and log those errors, showing a fallback UI.
  2. Handling Null or Undefined Data: You should always assume that the data might be null or undefined, especially when working with asynchronous data. It’s a good practice to ensure that you check for these cases before performing operations on the data.
  3. Data Caching and Optimization: If your data doesn’t change frequently, you might want to consider implementing caching to avoid unnecessary API calls.

Final Thoughts

Dealing with production errors like TypeError: "e.forEach is not a function" is an important part of the development process. By adding proper data validation, such as checking whether data is an array before calling forEach, you can prevent runtime errors and ensure your application runs smoothly in production.

Related blog posts