Reputation: 5
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
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
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
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