Search code examples
javascriptreactjsdictionaryoperator-keywordternary

Ternary operator in map() react


I made an application in React that shows state holidays using state codes. Now I'm wondering how to handle error when a user types in a non-existent state code or more characters. Here's my try to do it.

import React from "react";

const Holidays = props => {
if (props.dataLoaded === false) {
    return null;
  } else {
return (
  <div className="table-responsive">
    <table>
      <thead>
        <tr>
          <th>Holiday</th>
          <th>Date</th>
          <th>Type</th>
        </tr>
      </thead>
      <tbody>
        {props.country.map((country, index) =>
          country && index === undefined ? (
            <p>{props.error}</p>
          ) : (
            <React.Fragment key={index}>
              <tr>
                <td className="holiday-name">
                  <strong>{country.name}</strong>
                  <br />
                  <p>{country.description}</p>
                </td>
                <td className="holiday-date">
                  {country.date.datetime.day}.{country.date.datetime.month}.
                  {country.date.datetime.year}
                </td>
                <td className="holiday-type">
                  {country.type[0]} {country.type[1]}
                </td>
              </tr>
            </React.Fragment>
        )
          )}
          </tbody>
         </table>
       </div>
      );
    }
      };

 export default Holidays;

But with this code when a non-existing state code or more characters is typed in, it throws a error "TypeError: Cannot read property 'map' of undefined".Any help is acceptable


Solution

  • The issue with your code is that your data props.country is being mapped before the data is defined, and as we know the map method .map() can only be used on arrays. Check here for more details. Therefore when you try use map on something that is undefined it will throw that error.

    The reason that it is undefined at first is in most scenarios because you are fetching the data from the backend either from a server or from some public API. Before the operation of the data that is being fetched is complete your props.country will be undefined or whatever you initialize it to.

    What you should do is change your current code of:

    {props.country.map((country, index) =>
              country && index === undefined ? (
                <p>{props.error}</p>
              ) : (
                <React.Fragment key={index}>
                  <tr>
                    <td className="holiday-name">
                      <strong>{country.name}</strong>
                      <br />
                      <p>{country.description}</p>
                    </td>
                    <td className="holiday-date">
                      {country.date.datetime.day}.{country.date.datetime.month}.
                      {country.date.datetime.year}
                    </td>
                    <td className="holiday-type">
                      {country.type[0]} {country.type[1]}
                    </td>
                  </tr>
                </React.Fragment>
            )
    )}
    

    To something like below, where we make a check of your props.country and make sure that it's loaded - once it is, then try use map on it to render out the contents.

    {props.country ? props.country.map((country, index) =>
              country && index === undefined ? (
                <p>{props.error}</p>
              ) : (
                <React.Fragment key={index}>
                  <tr>
                    <td className="holiday-name">
                      <strong>{country.name}</strong>
                      <br />
                      <p>{country.description}</p>
                    </td>
                    <td className="holiday-date">
                      {country.date.datetime.day}.{country.date.datetime.month}.
                      {country.date.datetime.year}
                    </td>
                    <td className="holiday-type">
                      {country.type[0]} {country.type[1]}
                    </td>
                  </tr>
                </React.Fragment>
            )
    ): <div>Data Loading!</div>}
    

    So what we are doing here is adding another ternary conditional statement to check if props.country is true THEN apply the map method, else you simply return the div with contents "Data Loading!" before the data is loaded.

    To clarify we are adding props.country ? right at the start of your code inside the opening curly brace { and : <div>Data Loading!</div> at the end right after the last closing bracket ) and before the closing curly brace }.

    Now this <div>Data Loading!</div> could be anything you don't have to put a div or text you could simply put null, but before the data is fetched in those split seconds there will be empty space where your output would be.

    Experiment with what you want to put there, but something nice would be to put a loader like a spinner or a styled message to indicate that your data is being fetched.

    Edit: You could also initialize your props.country to an array, so that using .map on it does not resolve into an error automatically even when it's empty. If this was passed down as a props from your parent component's state, simply do:

    state = {
       country: []
    }
    

    The prior methods to solving this problem are better, though. But you should still probably get used to initializing your state properties with the data that they will eventually hold.