React: TypeError: places.map is not a function
React: TypeError: places.map is not a function
正在尝试使用 React 和 google 地图 api 构建一个靠近您的位置网页。到目前为止,我能够使用 rapidapi (Trailapi) 根据我的位置使用 axios get 请求获取 API 数据。我目前存储了我所在州的徒步旅行路线列表,并将它们传递给了我的列表组件。当尝试在我的 placedetails 组件中显示我的新列表时,它抛出一个 TypeError: places.map is not a function。数据已更改为可以传递到 .map 的数组,但我仍然收到错误消息,即传入的数据在我的 List.js 文件中的类型不正确。
示例响应(扫描我附近的简单位置时的数据输出)
{22965: {…}, 22972: {…}}
22965: {name: 'Coyote Hills Regional Park', city: 'Fremont', state: 'California', country: 'United States', description: 'Excellent trails.'}
22972: {name: 'Garin/Dry Creek Pioneer Regional Parks', city: 'Hayward', state: 'California', country: 'United States', description: 'The 20-mile system of trails within Garin and Dry …re suitable for bicycles. Please, no motorcycles.'}
index.js
import axios from 'axios';
export const getPlacesData = async (sw) => {
try {
const { data } = await axios.get('https://trailapi-trailapi.p.rapidapi.com/activity/',
{
params: {
lat: sw.lat,
limit: '10',
lon: sw.lng,
'q-state_cont': 'California',
radius: '7',
'q-activities_activity_type_name_eq': 'hiking'
},
headers: {
'x-rapidapi-host': 'trailapi-trailapi.p.rapidapi.com',
'x-rapidapi-key': '19c3948709msh555b77273198f2ap102f38jsn78f1f9fd6b2e'
}
});
console.log(data)
return data;
} catch (error) {
console.log(error)
}
}
App.js
import React, {useState,useEffect} from 'react';
import {CssBaseline, Grid} from '@material-ui/core';
import {getPlacesData} from '../api/index';
import List from './List/List';
import Map from './Map/Map';
function Browser() {
const [places,setPlaces] = useState([]);
const [coordinates,setCoordinates] = useState({});
const [bounds,setBounds] = useState({});
useEffect(() => {
navigator.geolocation.getCurrentPosition(({coords: {latitude, longitude}}) => {
setCoordinates({lat: latitude,lng: longitude});
})
}, []);
useEffect(() => {
getPlacesData(bounds.sw)
.then((data) => {
setPlaces(data);
})
}, [coordinates,bounds])
return (
<>
<CssBaseline />
<Grid container spacing={3} style={{width: '100%'}}>
<Grid item xs={12} md={4}>
<List places = {places} />
</Grid>
<Grid item xs={12} md={4} style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
<Map
setCoordinates={setCoordinates}
setBounds={setBounds}
coordinates={coordinates}
places={places}
/>
</Grid>
</Grid>
</>
);
}
export default App;
Map.jsx
import React from 'react';
import GoogleMapReact from 'google-map-react';
import {Paper,Typography,useMediaQuery} from '@material-ui/core';
import LocationOnOutlinedIcon from '@material-ui/icons/LocationOnOutlined';
import Rating from '@material-ui/lab/Rating';
import useStyles from './mapstyles';
const Map = ({setCoordinates,setBounds,coordinates}) => {
const classes = useStyles();
const isMobile = useMediaQuery('(min-width:600px)');
return (
<div className={classes.mapContainer} style={{height: '90%', width: '300%' }}>
<GoogleMapReact
bootstrapURLKeys={{ key:'AIzaSyDHTnVQEJGsaDcfMHdP2SGlJDixP83s9f8'}}
defaultCenter={coordinates}
center={coordinates}
defaultZoom={14}
margin= {[50,50,50,50]}
options={''}
onChange={(e) => {
setCoordinates({lat:e.center.lat,lng:e.center.lng});
setBounds({ne: e.marginBounds.ne, sw: e.marginBounds.sw});
}}
onChildClick={''}
>
</GoogleMapReact>
</div>
);
}
export default Map;
List.jsx
import React, {useState} from 'react';
import { CircularProgress, Grid, Typography, InputLabel, MenuItem, FormControl, Select } from '@material-ui/core';
import PlaceDetails from '../PlaceDetails/PlaceDetails';
import useStyles from './liststyles';
const List = ({places}) => {
const classes = useStyles();
const [type,setType] = useState('restaurants');
const [rating,setRating] = useState('');
return (
<div className={classes.container}>
<Typography variant='h4'>
Trails all around you!
</Typography>
<FormControl className={classes.formControl}>
<InputLabel>Type</InputLabel>
<Select value={type} onChange={(e) => setType(e.target.value)}>
<MenuItem value="restaurants">Hiking</MenuItem>
<MenuItem value="hotels"> Biking</MenuItem>
</Select>
</FormControl>
<FormControl className={classes.formControl}>
<InputLabel>Rating</InputLabel>
<Select value={rating} onChange={(e) => setRating(e.target.value)}>
<MenuItem value={0}>All</MenuItem>
<MenuItem value={1}>1.0</MenuItem>
<MenuItem value={2}>2.0</MenuItem>
<MenuItem value={3}>3.0</MenuItem>
<MenuItem value={4}>4.0</MenuItem>
<MenuItem value={5}>5.0</MenuItem>
</Select>
</FormControl>
<Grid container spacing={3} className={classes.list}>
{places?.map((place, i) => (
<Grid item key={i} xs={12}>
<PlaceDetails place={place}/>
</Grid>
))}
</Grid>
</div>
);
}
export default List;
PlaceDetails.jsx
import React from 'react';
const PlaceDetails = ({place}) => {
return (
<h1> {place.name} </h1>
);
}
export default PlaceDetails
以及随附的样式文件
liststyles.js
import { makeStyles } from '@material-ui/core/styles';
export default makeStyles((theme) => ({
formControl: {
margin: theme.spacing(1), minWidth: 120, marginBottom: '30px',
},
selectEmpty: {
marginTop: theme.spacing(2),
},
loading: {
height: '600px', display: 'flex', justifyContent: 'center', alignItems: 'center',
},
container: {
padding: '25px',
},
marginBottom: {
marginBottom: '30px',
},
list: {
height: '75vh', overflow: 'auto',
},
}));
mapstyles.js
import { makeStyles } from '@material-ui/core/styles';
export default makeStyles(() => ({
paper: {
padding: '10px', display: 'flex', flexDirection: 'column', justifyContent: 'center', width: '100px',
},
mapContainer: {
top: '1000px',
},
markerContainer: {
position: 'absolute', transform: 'translate(-50%, -50%)', zIndex: 1, '&:hover': { zIndex: 2 },
},
pointer: {
cursor: 'pointer',
},
}));
首先,如果您想使用map()
,您需要确保places
是一个数组。它甚至可以应用于对象数组。
例如,在下面的代码片段中,jsonResponse
中有 json 个数据,您希望根据其高度过滤数据。您可以尝试输入数字并对其进行过滤。
var jsonResponse = {
status: "OK",
code: "200",
data: [
{name: "Josh", height: 190},
{name: "Mia", height: 185},
{name: "Kevin", height: 180}
]};
function filterData(h) {
var wrapper = $('.wrapper');
wrapper.html('');
var result = jsonResponse?.data?.map((d, i) => d.height > h ? `<div id="${i}">${d.name}</div><br>` : '');
wrapper.append(result);
}
$('#filter').click(e => {
var h = $('#inputheight').val();
filterData(h);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input name="height" id="inputheight" type="number" min="0" value="0">
<button id="filter">Filter</button>
<div class="wrapper">
</div>
所以你的初始 places
状态是一个空数组:
const [places, setPlaces] = useState([]);
但响应似乎是一个对象,您随后更新到 places
状态并传递给 List
组件以映射到 PlaceDetails
组件。
如果没有其他组件使用 places
状态,那么我建议在设置状态时将响应 data
转换为数组。 Object.values 可以将对象转换为对象值的数组。
const data = {
22965: {
name: 'Coyote Hills Regional Park',
city: 'Fremont',
state: 'California',
country: 'United States',
description: 'Excellent trails.'
},
22972: {
name: 'Garin/Dry Creek Pioneer Regional Parks',
city: 'Hayward',
state: 'California',
country: 'United States',
description: 'The 20-mile system of trails within Garin and Dry …re suitable for bicycles. Please, no motorcycles.'
},
};
console.log(Object.values(data));
useEffect(() => {
getPlacesData(bounds.sw)
.then((data) => {
setPlaces(Object.values(data));
})
}, [coordinates, bounds]);
places
状态将更新为一个对象数组,其中 name
属性 可用。
[
{
"name": "Coyote Hills Regional Park", // *
"city": "Fremont",
"state": "California",
"country": "United States",
"description": "Excellent trails."
},
{
"name": "Garin/Dry Creek Pioneer Regional Parks", // *
"city": "Hayward",
"state": "California",
"country": "United States",
"description": "The 20-mile system of trails within Garin and Dry …re suitable for bicycles. Please, no motorcycles."
}
]
...
const PlaceDetails = ({ place }) => {
return (
<h1>{place.name}</h1> // *
);
};
正在尝试使用 React 和 google 地图 api 构建一个靠近您的位置网页。到目前为止,我能够使用 rapidapi (Trailapi) 根据我的位置使用 axios get 请求获取 API 数据。我目前存储了我所在州的徒步旅行路线列表,并将它们传递给了我的列表组件。当尝试在我的 placedetails 组件中显示我的新列表时,它抛出一个 TypeError: places.map is not a function。数据已更改为可以传递到 .map 的数组,但我仍然收到错误消息,即传入的数据在我的 List.js 文件中的类型不正确。
示例响应(扫描我附近的简单位置时的数据输出)
{22965: {…}, 22972: {…}}
22965: {name: 'Coyote Hills Regional Park', city: 'Fremont', state: 'California', country: 'United States', description: 'Excellent trails.'}
22972: {name: 'Garin/Dry Creek Pioneer Regional Parks', city: 'Hayward', state: 'California', country: 'United States', description: 'The 20-mile system of trails within Garin and Dry …re suitable for bicycles. Please, no motorcycles.'}
index.js
import axios from 'axios';
export const getPlacesData = async (sw) => {
try {
const { data } = await axios.get('https://trailapi-trailapi.p.rapidapi.com/activity/',
{
params: {
lat: sw.lat,
limit: '10',
lon: sw.lng,
'q-state_cont': 'California',
radius: '7',
'q-activities_activity_type_name_eq': 'hiking'
},
headers: {
'x-rapidapi-host': 'trailapi-trailapi.p.rapidapi.com',
'x-rapidapi-key': '19c3948709msh555b77273198f2ap102f38jsn78f1f9fd6b2e'
}
});
console.log(data)
return data;
} catch (error) {
console.log(error)
}
}
App.js
import React, {useState,useEffect} from 'react';
import {CssBaseline, Grid} from '@material-ui/core';
import {getPlacesData} from '../api/index';
import List from './List/List';
import Map from './Map/Map';
function Browser() {
const [places,setPlaces] = useState([]);
const [coordinates,setCoordinates] = useState({});
const [bounds,setBounds] = useState({});
useEffect(() => {
navigator.geolocation.getCurrentPosition(({coords: {latitude, longitude}}) => {
setCoordinates({lat: latitude,lng: longitude});
})
}, []);
useEffect(() => {
getPlacesData(bounds.sw)
.then((data) => {
setPlaces(data);
})
}, [coordinates,bounds])
return (
<>
<CssBaseline />
<Grid container spacing={3} style={{width: '100%'}}>
<Grid item xs={12} md={4}>
<List places = {places} />
</Grid>
<Grid item xs={12} md={4} style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
<Map
setCoordinates={setCoordinates}
setBounds={setBounds}
coordinates={coordinates}
places={places}
/>
</Grid>
</Grid>
</>
);
}
export default App;
Map.jsx
import React from 'react';
import GoogleMapReact from 'google-map-react';
import {Paper,Typography,useMediaQuery} from '@material-ui/core';
import LocationOnOutlinedIcon from '@material-ui/icons/LocationOnOutlined';
import Rating from '@material-ui/lab/Rating';
import useStyles from './mapstyles';
const Map = ({setCoordinates,setBounds,coordinates}) => {
const classes = useStyles();
const isMobile = useMediaQuery('(min-width:600px)');
return (
<div className={classes.mapContainer} style={{height: '90%', width: '300%' }}>
<GoogleMapReact
bootstrapURLKeys={{ key:'AIzaSyDHTnVQEJGsaDcfMHdP2SGlJDixP83s9f8'}}
defaultCenter={coordinates}
center={coordinates}
defaultZoom={14}
margin= {[50,50,50,50]}
options={''}
onChange={(e) => {
setCoordinates({lat:e.center.lat,lng:e.center.lng});
setBounds({ne: e.marginBounds.ne, sw: e.marginBounds.sw});
}}
onChildClick={''}
>
</GoogleMapReact>
</div>
);
}
export default Map;
List.jsx
import React, {useState} from 'react';
import { CircularProgress, Grid, Typography, InputLabel, MenuItem, FormControl, Select } from '@material-ui/core';
import PlaceDetails from '../PlaceDetails/PlaceDetails';
import useStyles from './liststyles';
const List = ({places}) => {
const classes = useStyles();
const [type,setType] = useState('restaurants');
const [rating,setRating] = useState('');
return (
<div className={classes.container}>
<Typography variant='h4'>
Trails all around you!
</Typography>
<FormControl className={classes.formControl}>
<InputLabel>Type</InputLabel>
<Select value={type} onChange={(e) => setType(e.target.value)}>
<MenuItem value="restaurants">Hiking</MenuItem>
<MenuItem value="hotels"> Biking</MenuItem>
</Select>
</FormControl>
<FormControl className={classes.formControl}>
<InputLabel>Rating</InputLabel>
<Select value={rating} onChange={(e) => setRating(e.target.value)}>
<MenuItem value={0}>All</MenuItem>
<MenuItem value={1}>1.0</MenuItem>
<MenuItem value={2}>2.0</MenuItem>
<MenuItem value={3}>3.0</MenuItem>
<MenuItem value={4}>4.0</MenuItem>
<MenuItem value={5}>5.0</MenuItem>
</Select>
</FormControl>
<Grid container spacing={3} className={classes.list}>
{places?.map((place, i) => (
<Grid item key={i} xs={12}>
<PlaceDetails place={place}/>
</Grid>
))}
</Grid>
</div>
);
}
export default List;
PlaceDetails.jsx
import React from 'react';
const PlaceDetails = ({place}) => {
return (
<h1> {place.name} </h1>
);
}
export default PlaceDetails
以及随附的样式文件
liststyles.js
import { makeStyles } from '@material-ui/core/styles';
export default makeStyles((theme) => ({
formControl: {
margin: theme.spacing(1), minWidth: 120, marginBottom: '30px',
},
selectEmpty: {
marginTop: theme.spacing(2),
},
loading: {
height: '600px', display: 'flex', justifyContent: 'center', alignItems: 'center',
},
container: {
padding: '25px',
},
marginBottom: {
marginBottom: '30px',
},
list: {
height: '75vh', overflow: 'auto',
},
}));
mapstyles.js
import { makeStyles } from '@material-ui/core/styles';
export default makeStyles(() => ({
paper: {
padding: '10px', display: 'flex', flexDirection: 'column', justifyContent: 'center', width: '100px',
},
mapContainer: {
top: '1000px',
},
markerContainer: {
position: 'absolute', transform: 'translate(-50%, -50%)', zIndex: 1, '&:hover': { zIndex: 2 },
},
pointer: {
cursor: 'pointer',
},
}));
首先,如果您想使用map()
,您需要确保places
是一个数组。它甚至可以应用于对象数组。
例如,在下面的代码片段中,jsonResponse
中有 json 个数据,您希望根据其高度过滤数据。您可以尝试输入数字并对其进行过滤。
var jsonResponse = {
status: "OK",
code: "200",
data: [
{name: "Josh", height: 190},
{name: "Mia", height: 185},
{name: "Kevin", height: 180}
]};
function filterData(h) {
var wrapper = $('.wrapper');
wrapper.html('');
var result = jsonResponse?.data?.map((d, i) => d.height > h ? `<div id="${i}">${d.name}</div><br>` : '');
wrapper.append(result);
}
$('#filter').click(e => {
var h = $('#inputheight').val();
filterData(h);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input name="height" id="inputheight" type="number" min="0" value="0">
<button id="filter">Filter</button>
<div class="wrapper">
</div>
所以你的初始 places
状态是一个空数组:
const [places, setPlaces] = useState([]);
但响应似乎是一个对象,您随后更新到 places
状态并传递给 List
组件以映射到 PlaceDetails
组件。
如果没有其他组件使用 places
状态,那么我建议在设置状态时将响应 data
转换为数组。 Object.values 可以将对象转换为对象值的数组。
const data = {
22965: {
name: 'Coyote Hills Regional Park',
city: 'Fremont',
state: 'California',
country: 'United States',
description: 'Excellent trails.'
},
22972: {
name: 'Garin/Dry Creek Pioneer Regional Parks',
city: 'Hayward',
state: 'California',
country: 'United States',
description: 'The 20-mile system of trails within Garin and Dry …re suitable for bicycles. Please, no motorcycles.'
},
};
console.log(Object.values(data));
useEffect(() => {
getPlacesData(bounds.sw)
.then((data) => {
setPlaces(Object.values(data));
})
}, [coordinates, bounds]);
places
状态将更新为一个对象数组,其中 name
属性 可用。
[
{
"name": "Coyote Hills Regional Park", // *
"city": "Fremont",
"state": "California",
"country": "United States",
"description": "Excellent trails."
},
{
"name": "Garin/Dry Creek Pioneer Regional Parks", // *
"city": "Hayward",
"state": "California",
"country": "United States",
"description": "The 20-mile system of trails within Garin and Dry …re suitable for bicycles. Please, no motorcycles."
}
]
...
const PlaceDetails = ({ place }) => {
return (
<h1>{place.name}</h1> // *
);
};