简体   繁体   中英

React keeps saying .map is not a function and nothing I have tried is working?

I have a component that gets listings from and API using axios. When the home page first loads all the listings are retrieved and load fine, but when I go to say the listing detail page and back to the home page I get a 'listings.map is not a function' error, when I refresh the page I do not get the error and the listings load.

Here is the component(tried using a loading boolean but did nothing):

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { getListings } from '../../actions/listings';
import { Link as RouteLink } from 'react-router-dom';
import PropTypes from 'prop-types';
//material ui
import {
  Container,
  Typography,
  Button,
  Card,
  CardActions,
  CardActionArea,
  CardContent,
  CardMedia,
  Grid,
  Link,
} from '@material-ui/core';
import { withStyles } from '@material-ui/core/styles';

const useStyles = {
  listings: {
    marginTop: '100px',
    backgroundColor: 'white',
  },
  title: {
    marginBottom: '15px',
  },
  listImg: {
    width: '250px',
    height: '250px',
  },
  card: {
    width: '275px',
    height: '300px',
  },
  media: {
    height: 140,
  },
};

class Listings extends Component {
  static propTypes = {
    listings: PropTypes.array.isRequired,
    isLoading: PropTypes.bool,
  };

  componentDidMount() {
    this.props.getListings();
  }

  render() {
    const { classes, listings } = this.props;
    return (
      <>
        <Container maxWidth='lg' className={classes.listings}>
          <Typography align='center' variant='h1' className={classes.title}>
            Recent Listings
          </Typography>
          <Grid container justify='center' spacing={8}>
            {listings &&
              listings.map((listing, index) => (
                <Grid key={index} item>
                  <Link component={RouteLink} to={`/listing/${listing._id}`}>
                    <Card className={classes.card}>
                      <CardActionArea>
                        <CardMedia
                          className={classes.media}
                          title='Contemplative Reptile'
                          image={listing.imgUrls[0]}
                        />
                        <CardContent>
                          <Typography gutterBottom variant='h6' component='h2'>
                            {listing.title}
                          </Typography>
                          <Typography
                            variant='body2'
                            color='textSecondary'
                            component='p'
                          >
                            {listing.price} /night
                          </Typography>
                        </CardContent>
                      </CardActionArea>
                      <CardActions>
                        <Button size='small' color='primary'>
                          BOOK
                        </Button>
                      </CardActions>
                    </Card>
                  </Link>
                </Grid>
              ))}
          </Grid>
        </Container>
      </>
    );
  }
}

//sets component props to the app state
const mapStateToProps = (state) => ({
  listings: state.listings.listings,
  isLoading: state.listings.isLoading,
});

//withStyles is used for material-ui styles (using class components)
export default connect(mapStateToProps, { getListings })(
  withStyles(useStyles)(Listings)
);

here is the getListings action:

export const getListings = () => (dispatch) => {
  axiosInstance
    .get('/listings')
    .then((res) => {
      dispatch({
        type: GET_LISTINGS,
        payload: res.data,
      });
    })
    .catch((err) => console.log(err));
};

and the reducer:

const initialState = {
  listings: [],
  isLoading: true,
};

export default function (state = initialState, action) {
  switch (action.type) {
    case GET_LISTINGS:
      return {
        ...state,
        listings: action.payload,
        isLoading: false,
      };
    case GET_LISTING:
      return {
        ...state,
        listings: action.payload,
        isLoading: false,
      };
    case CREATE_LISTING:
      return {
        ...state,
        listings: [...state.listings, action.payload],
      };
    case CLEAR_LISTINGS:
      return {
        ...state,
        listings: [],
      };
    default:
      return state;
  }
}

Just don't get how it does work, but then doesn't at times. Listings are declared as an array also in initialState.

If your listings variable type is

Object

then you have to map like this:

const object1 = {
  a: 'somestring',
  b: 42
};

for (const [key, value] of Object.entries(object1)) {
  console.log(`${key}: ${value}`);
}

// expected output:
// "a: somestring"
// "b: 42"
// order is not guaranteed

On the other hand if your variable is

Array

then:

const numbers = [2, 4, 6, 8];
const squares = numbers.map(number => number * numbers);

console.log(squares);
// output: Array [4, 16, 36, 64]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM