BigJobbies
BigJobbies

Reputation: 4055

Mapping array in React gives .map is not a function

I am having troubles trying to output my errors in react. the data is coming from an external API, and is formatted like so;

{
    "message": "The given data was invalid.",
    "errors": {
        "name": [
            "The name field is required."
        ],
        "address": [
            "The address field is required."
        ],
        "postcode": [
            "The postcode field is required."
        ]
    }
}

My page which returns the API data and the error component looks something like this;

const ShowData = () => {
    const [errorData, setErrorData] = useState([]);

    const onSubmit = handleSubmit(async ({ name, address, postcode }) => {
        if (errorData) setErrorData([]);

        try {
            // Call API and process here
        } catch (error) {
            setErrorData(error.data);
        }
    });

    return (
        <div>
            {errorData && !Object.keys(errorData).length == 0 &&
                <ErrorComponent errorData={errorData} />
            }
        </div>
    )
}

export default ShowData;


class ErrorComponent extends Component {
    render() {
        const errorData = this.props.errorData;

        return (
            <div>{errorData.message}</div>
            <ul>
                {errorData.errors.map(error => {
                    <li>{error}</li>
                })}
            </ul>
        )
    }
}

export default ErrorComponent;

The error i am receiving is;

errorData.errors.map is not a function

The following is what i am trying to achieve;

<div>The given data was invalid.</div>
<ul>
    <li>The name field is required.</li>
    <li>The address field is required.</li>
    <li>The postcode field is required.</li>
</ul>

Upvotes: 1

Views: 750

Answers (3)

hgb123
hgb123

Reputation: 14891

First, you cannot use map on objects, because map itself is a method of Array. As you can see in the doc, it is Array.prototype.map. So here you could transform the errors to array before mapping. Here I use Object.entries because I also want to display the field that caused error.

There are others problems rather than just iterating the errorData:

  • you returned 2 adjacent elements, div and ul. you must wrap it into one element only. You could use div to wrap, but I prefer using React.Fragment
  • on mapping, you don't return anything to render, so in your example, it will return a list of undefined, which would display nothing. Solution here is to use explicit return, or shorthand with () => (<yourelement />)
class ErrorComponent extends React.Component {
  render() {
    const errorData = this.props.errorData;

    return (
      <React.Fragment>
        <div>{errorData.message}</div>
        <ul>
          {Object.entries(errorData.errors).map(
            ([errorField, errorMessages]) => (
              <li>
                {errorField}: {errorMessages[0]}
              </li>
            )
          )}
        </ul>
      </React.Fragment>
    );
  }
}

Live demo:

Edit dry-haze-mxsg3

Upvotes: 1

codemonkey
codemonkey

Reputation: 7915

2 problems.

  1. You are trying to map through an object. You should instated do:
   <ul>
         {Object.values(errorData.errors).map((e, i) =>
               <li key={i}>{e[0]}</li>
         )}
   </ul>
  1. Your map function is missing the return statement, which you can include implicitly by dropping those curly braces as shown above.

Upvotes: 2

AndrewL
AndrewL

Reputation: 112

You're trying to run .map() on an object. .map() is only usable on iterables.

You might try creating an array from the object using Object.keys, Object.values, Object.entries, or another method that creates an array from an object.

Upvotes: 1

Related Questions