TypeError:未定义不是一个对象(评估“movies.map”)
我正在使用 REACT 构建电影 API,当我在本地加载页面时,我不断收到错误消息“TypeError:undefined 不是对象(评估‘movies.map’)”。
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>}
我不确定 movies.map 发生了什么。我查了一下可能是什么问题,但没有找到。除了此部分,我网页上的其他所有内容都正常工作。
<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,
}),
};
您没有将
movies
prop 传递给组件,因此 movies 未定义,而
undefined
没有 map 方法。
我不清楚您的两个代码示例之间的关系,但是……
您有三个名为“movies”的独立变量。
-
一个通过
this.state = { movies: []
处于状态中
-
另一个从 props 获得:
const { movies } = this.props
-
第三个在您的 map 函数中:
movies.map((movies) =>
{})`
这是我们对错误感兴趣的第二个。 (第一个从未被引用,第三个造成了混淆,但没有实际问题。)
为了使
this.props.movi​​es
成为一个数组,组件必须将其作为 prop 接收。
这是您的组件的简化版本,已精简以隔离 movies.map 问题:
class MyComponent extends React.Component {
render () {
const {movies} = this.props;
return (
movies.map(m => <li>{m}</li>);
);
}
}'
如果您将数组作为 prop 传递,则一切都很好:
const movieArray = ['x', 'y', 'z'];
<MyComponent movies={movieArray} />
如果您省略 prop,它会爆炸,因为
this.props.movi​​es
未定义:
// blows up because this.props.movies isn't
// defined so you can't call this.props.movies.map
<MyComponent />
我不知道您是否有意使用
this.props.movi​​es
而不是
this.state.movi​​es
,但这就是它爆炸的原因。
您可以使用 可选链接 :
movies?.map?.(m => {})
使用此语法,如果
movies
未定义或者没有
map
方法,它将不会尝试调用它并且不会引发错误。
您破坏了组件的核心原则之一,该原则与 Concert 是分开的,您当前的组件负责获取数据以及渲染数据。此组件应仅获取数据,而其他组件应渲染数据,但最简单的解决方法是验证您的数据是否存在(如果存在),则通过它进行映射
{FavoriteMovies.length > 0 && movies && movies.map((movies)}