kamomidnite
kamomidnite

Reputation: 5

TypeError: props.routes.map is not a function

I'm new to react-redux. I am trying to pull data from Asp.Net Core Web Api to my react project.

I get an error when mapping the data I receive with redux in react.

I see the data coming in redux DevTools. State is filling up.

import React, { useEffect } from "react";
import * as actions from "../redux/actions/routeActions";
import { connect } from "react-redux";
import PropTypes from 'prop-types'

function Data({ ...props }) {
  useEffect(() => {
    props.fetchRoutes();
  });

    function renderData() {
        return props.routes.map((route) => {
            const { id, departureLocation, arrivalLocation, distance } = route
            return (
                <tr key={id}>
                    <td>{id}</td>
                    <td>{departureLocation}</td>
                    <td>{arrivalLocation}</td>
                    <td>{distance}</td>
                </tr>
            )
        })
    }
    return(
        < div >
            <table className="table">
                <thead>
                    <tr>
                        <th />
                        <th>departureLocation</th>
                        <th>arrivalLocation</th>
                        <th>Distance</th>
                    </tr>
                </thead>
                <tbody>
                    {renderData()}
                </tbody>
            </table>
        </div >
    );
}

Data.propTypes = {
    routes: PropTypes.array.isRequired
}

const mapStateToProps = (state) => ({
  routes: state.routes,
});

const mapDispatchToProps = {
  fetchRoutes: actions.fetchAll,
};

export default connect(mapStateToProps, mapActionToProps)(Data);

And i get this error.

TypeError: props.routes.map is not a function.

I couldn't find where I made a mistake.

Upvotes: 0

Views: 1262

Answers (3)

katma
katma

Reputation: 319

In addition to other answers you can also make your renderData to handle routes more safe like this :

    function renderData() {
        return (props.routes || []).map((route) => {
            const { id, departureLocation, arrivalLocation, distance } = route
            return (
                <tr key={id}>
                    <td>{id}</td>
                    <td>{departureLocation}</td>
                    <td>{arrivalLocation}</td>
                    <td>{distance}</td>
                </tr>
            )
        })
    }

Upvotes: 1

Gustavo
Gustavo

Reputation: 75

Careful as you are fetching your routes in an effect without dependencies.

useEffect(() => {
  props.fetchRoutes();
});

This might enter an infinite loop as you are fetching routes every time your component re-renders.

To ensure that does not happen add a dependency to the useEffect:

useEffect(() => {
  props.fetchRoutes();
}, [props.fetchRoutes]);

This will only enter the useEffect (besides the first render) whenever the fetchRoutes variables changes, which should once at a maximum. You could use [] to do it like it was ComponentDidMount. If you're using react hooks eslint it won't let you without a warning though. You can read more about the useEffect hook here.

Regarding the not a function part, the reason is likely to be that your data still isn't there when the first render happens (as you still need to fetch your data) and therefore it will try to do a map of an undefined.

As said in other comments one option is to only map through the variable if the variable has any value. The other would be to destructure your props and default the variable to [].

function Data({ fetchRoutes, routes = [] }) {
  ...
}

This way you also won't need the props. part.

Upvotes: 0

Kerem atam
Kerem atam

Reputation: 2787

It because routes are not ready on first render.

Rendering with condition will ensure that you dont map into routes that does not exist.

<tbody>
  {props.routes && props.renderData()}
</tbody>

Better solution would be setting your routes initial value in reducer of redux as an empty array.

Upvotes: 2

Related Questions