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> // * 
  );
};