Home > database >  TypeError: undefined is not an object (evaluating 'movies.map')
TypeError: undefined is not an object (evaluating 'movies.map')

Time:10-02

I'm building a movie api with REACT, and I keep getting the error message "TypeError: undefined is not an object (evaluating 'movies.map')" when I load my page locally.

  import React from 'react';
    import axios from 'axios';
    import PropTypes from 'prop-types';
    import { Button, Card, CardDeck, Form, Row } from 'react-bootstrap';
    import './profile-view.scss';
    
    export class ProfileView extends React.Component {
      constructor() {
        super();
    
        this.state = {
          Name: null,
          Username: null,
          Password: null,
          Email: null,
          Birthdate: null,
          FavoriteMovies: [],
          validated: null,
          movies: [],
        };
      }
    
      componentDidMount() {
        const accessToken = localStorage.getItem('token');
        if (accessToken !== null) {
          this.getUser(accessToken);
        }
      }
    
    
      // get user method
      getUser(token) {
        const username = localStorage.getItem('user');
        axios.get(`https://nhas-flixdb-2021.herokuapp.com/users/${username}`, {
          headers: { Authorization: `Bearer ${token}` },
        })
          .then((response) => {
            this.setState({
              Name: response.data.Name,
              Username: response.data.Username,
              Password: response.data.Password,
              Email: response.data.Email,
              Birthdate: response.data.Birthdate,
              FavoriteMovies: response.data.FavoriteMovies,
            });
          })
          .catch(function (error) {
            console.log(error);
          });
      }
    
    
      removeFavoriteMovie() {
        const token = localStorage.getItem('token');
        const username = localStorage.getItem('user');
    
        
        axios.delete(`https://nhas-flixdb-2021.herokuapp.com/users/${username}/movies/${movies._id}`, {
            headers: { Authorization: `Bearer ${token}` },
          })
          
          .then(() => {
            alert('Movie was removed');
            this.componentDidMount();
          })
          .catch(function (error) {
            console.log(error);
          })
        // .then(() => window.location.reload());
      }
    
      handleUpdate(e, newName, newUsername, newPassword, newEmail, newBirthdate) {
        this.setState({
          validated: null,
        });
    
        const form = e.currentTarget;
        if (form.checkValidity() === false) {
          e.preventDefault();
          e.stopPropagation();
          this.setState({
            validated: true,
          });
          return;
        }
        e.preventDefault();
    
        const token = localStorage.getItem('token');
        const username = localStorage.getItem('user');
    
        axios.put(`https://nhas-flixdb-2021.herokuapp.com/users/${username}`, {
          headers: { Authorization: `Bearer ${token}` },
          data: {
            Name: newName ? newName : this.state.Name,
            Username: newUsername ? newUsername : this.state.Username,
            Password: newPassword ? newPassword : this.state.Password,
            Email: newEmail ? newEmail : this.state.Email,
            Birthdate: newBirthdate ? newBirthdate : this.state.Birthdate,
          },
        })
          .then((response) => {
            alert('Saved Changes');
            this.setState({
              Name: response.data.Name,
              Username: response.data.Username,
              Password: response.data.Password,
              Email: response.data.Email,
              Birthdate: response.data.Birthdate,
            });
            localStorage.setItem('user', this.state.Username);
            window.open(`/users/${username}`, '_self');
          })
          .catch(function (error) {
            console.log(error);
          });
      }
      setName(input) {
        this.Name = input;
      }
    
      setUsername(input) {
        this.Username = input;
      }
    
      setPassword(input) {
        this.Password = input;
      }
    
      setEmail(input) {
        this.Email = input;
      }
    
      setBirthdate(input) {
        this.Birthdate = input;
      }
    
      handleDeleteUser(e) {
        e.preventDefault();
    
        const token = localStorage.getItem('token');
        const username = localStorage.getItem('user');
    
        axios.delete(`https://nhas-flixdb-2021.herokuapp.com/users/${username}`, {
          headers: { Authorization: `Bearer ${token}` },
        })
          .then(() => {
            localStorage.removeItem('user');
            localStorage.removeItem('token');
            alert('Your account has been deleted.');
            window.open(`/`, '_self');
          })
          .catch((e) => {
            console.log(e);
          });
      }
    
      render() {
        const { FavoriteMovies, validated} = this.state;
        const { movies } = this.props;
        
        return (
          <Row className="profile-view">
            <Card className="profile-card">
              <h1 className="text-center">Your Account</h1>
              <h2>Your Favorites Movies</h2>
              <Card.Body>
                {FavoriteMovies.length === 0 && <div className="text-center">Empty.</div>}

Im not sure what's going on with movies.map. I've looked up what might be the issue but no luck. everything else on my web page works exactly as it should except this section.

<div className="favorites-movies ">
              {FavoriteMovies.length > 0 &&
                movies.map((movies) => {
                  if (movies._id === FavoriteMovies.find((favMovie) => favMovie === movies._id)) {
                    return (
                      <CardDeck className="movie-card-deck">
                        <Card className="favorites-item card-content" style={{ width: '16rem' }} key={movies._id}>
                          <Card.Img style={{ width: '18rem' }} className="movieCard" variant="top" src={movies.ImagePath} />
                          <Card.Body>
                            <Card.Title className="movie-card-title">{movies.Title}</Card.Title>
                            <Button size='sm' className='profile-button remove-favorite' variant='danger' value={movies._id} onClick={(e) => this.removeFavoriteMovie(e, movies)}>
                              Remove
                            </Button>
                          </Card.Body>
                        </Card>
                      </CardDeck>
                    );
                  }
                })}
            </div>
          </Card.Body>
          <h3 className="section">Update Profile</h3>
          <Card.Body>
            <Form noValidate validated={validated} className="update-form" onSubmit={(e) => this.handleUpdate(e, this.Name, this.Username, this.Password, this.Email, this.Birthdate)}>

              <Form.Group controlId="formName">
                <Form.Label className="form-label">Name</Form.Label>
                <Form.Control type="text" placeholder="Change Name" onChange={(e) => this.setName(e.target.value)} />
              </Form.Group>

              <Form.Group controlId="formBasicUsername">
                <Form.Label className="form-label">
                  Username<span className="required">*</span>
                </Form.Label>
                <Form.Control type="text" placeholder="Change Username" onChange={(e) => this.setUsername(e.target.value)} />
              </Form.Group>

              <Form.Group controlId="formBasicPassword">
                <Form.Label className="form-label">
                  Password<span className="required">*</span>
                </Form.Label>
                <Form.Control type="password" placeholder="New Password" onChange={(e) => this.setPassword(e.target.value)} />
              </Form.Group>

              <Form.Group controlId="formBasicEmail">
                <Form.Label className="form-label">
                  Email<span className="required">*</span>
                </Form.Label>
                <Form.Control type="email" placeholder="Change Email" onChange={(e) => this.setEmail(e.target.value)} />
              </Form.Group>

              <Form.Group controlId="formBasicBirthday">
                <Form.Label className="form-label">Birthdate</Form.Label>
                <Form.Control type="date" placeholder="Change Birthdate" onChange={(e) => this.setBirthdate(e.target.value)} />
              </Form.Group>

              <Button variant='danger' type="submit">
                Update
              </Button>

              <h3>Delete your Account</h3>
              <Card.Body>
                <Button variant='danger' onClick={(e) => this.handleDeleteUser(e)}>
                  Delete Account
                </Button>
              </Card.Body>
            </Form>

          </Card.Body>
        </Card>
      </Row >
    );
  }
}

ProfileView.propTypes = {
  user: PropTypes.shape({
    FavoriteMovies: PropTypes.arrayOf(
      PropTypes.shape({
        _id: PropTypes.string.isRequired,
        Title: PropTypes.string.isRequired,
      })
    ),
    Username: PropTypes.string.isRequired,
    Email: PropTypes.string.isRequired,
    Birthdate: PropTypes.string,
  }),
};

CodePudding user response:

You broke one the of the core principles of components which is separate of concert s your current component is in charge of getting the data as well as rendering the data. This component should only get the data and other component should render the data, but the easiest fix is to validate if yow data exists if so map through it

{FavoriteMovies.length > 0 && movies &&  movies.map((movies)}

CodePudding user response:

You’re not passing a movies prop to the component, therefore movies is undefined, and undefined doesn’t have a map method.

It's not clear to me what the relationship is between your two code samples, but…

You have three separate variables named "movies".

  • One in state via this.state = { movies: [] }
  • Another that you get from props: const { movies } = this.props
  • And a third inside your map function: movies.map((movies) => {})`

It's the second one we're interested in regarding the error. (The first one is never referenced, and the third one creates confusion but no actual problems.)

In order for this.props.movies to be an array the component has to receive it as a prop.

Here's a simplified version of your component, stripped down to isolate the movies.map problem:

class MyComponent extends React.Component {
  render () {
    const {movies} = this.props;
    return (
      movies.map(m => <li>{m}</li>);
    );
  }
}'

If you pass an array as a prop, everything's fine:

const movieArray = ['x', 'y', 'z'];

<MyComponent movies={movieArray} />

If you omit the prop it blows up because this.props.movies is undefined:

// blows up because this.props.movies isn't
// defined so you can't call this.props.movies.map

<MyComponent />

I can't tell if it was your intent to use this.props.movies instead of this.state.movies but that's why it's blowing up.

You can guard against this problem with optional chaining:

movies?.map?.(m => {})

Using this syntax, if movies isn't defined or if it doesn't have a map method it won't try to invoke it and won't throw an error.

  • Related