这是我的组件,我想从API获取一些数据,然后将其传递给子项,但我收到错误未捕获的TypeError:无法读取属性' id'未定义的
我认为我做的一切都是正确的。在componentWillMount中我从moviedb API获取有关ID = 211672的电影的信息,然后在redux商店中设置关于电影的信息,而电影未定义,我正在显示simpile,当电影不是未定义的返回组件
请详细说明我做错了什么!
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import { getInfoAboutMovie } from '../../actions/movies.actions';
const AppWrapper = MainComponent =>
class extends Component {
state = {}
componentWillMount() {
this.props.getInfo(211672);
}
render() {
<div>
{this.props.movie === 'undefined' ?
<div> Do something while movie is not fetched </div> :
<MainComponent {...this.props} />}
</div>
);
}
};
const mapStateToProps = (state) => {
if (state.movies.movie !== undefined) {
return {
isFetching: state.movies.isFetching,
movie: state.movies.movie,
};
}
return {};
};
const mapDispatchToProps = dispatch => ({
getInfo: (movieId) => {
dispatch(getInfoAboutMovie(movieId));
},
});
const App = props =>
<div>{`Users: ${props.movie.id}`}</div>;
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(AppWrapper(App)));
Main.jsx 拥有以下路线
<Route exact path="/test" component={AppWrapper} />
movies.actions.js
import axios from 'axios';
import { API_KEY } from '../util.const';
export const REQUEST_MOVIES = 'REQUEST_MOVIES';
function requestPopularMovies(loadmore) {
return {
type: REQUEST_MOVIES,
loadmore,
};
}
export const INFO_ABOUT_MOVIE = 'INFO_ABOUT_MOVIE';
const infoAboutMovie = movie => ({
type: INFO_ABOUT_MOVIE,
movie,
});
export const getInfoAboutMovie = movieId => (dispatch) => {
dispatch(requestPopularMovies());
return axios.get(`https://api.themoviedb.org/3/movie/${movieId}?api_key=${API_KEY}&language=en-US`)
.then((res) => {
dispatch(infoAboutMovie(res.data));
});
};
res.data 我正在
{
"adult": false,
"backdrop_path": "/qLmdjn2fv0FV2Mh4NBzMArdA0Uu.jpg",
"belongs_to_collection": null,
"budget": 74000000,
"genres": [
{
"id": 10751,
"name": "Family"
},
{
"id": 16,
"name": "Animation"
},
{
"id": 12,
"name": "Adventure"
},
{
"id": 35,
"name": "Comedy"
}
],
"homepage": "http://www.minionsmovie.com/",
"id": 211672,
"imdb_id": "tt2293640",
"original_language": "en",
"original_title": "Minions",
"overview": "Minions Stuart, Kevin and Bob are recruited by Scarlet
Overkill,
a super-villain who, alongside her inventor husband Herb, hatches a plot to
take over the world.",
"popularity": 455.619878,
"poster_path": "/q0R4crx2SehcEEQEkYObktdeFy.jpg",
"production_companies": [
{
"name": "Universal Pictures",
"id": 33
},
{
"name": "Illumination Entertainment",
"id": 6704
}
],
"production_countries": [
{
"iso_3166_1": "US",
"name": "United States of America"
}
],
"release_date": "2015-06-17",
"revenue": 1156730962,
"runtime": 91,
"spoken_languages": [
{
"iso_639_1": "en",
"name": "English"
}
],
"status": "Released",
"tagline": "Before Gru, they had a history of bad bosses",
"title": "Minions",
"video": false,
"vote_average": 6.4,
}
movies.js (减速机)
import { RECEIVE_MOVIES, REQUEST_MOVIES, INFO_ABOUT_MOVIE, SEARCH_MOVIE } from '../actions/movies.actions';
const initMovies = [{
path: 'paths',
backdrop_path: '',
id: '',
original_title: '',
overview: '',
}];
function movies(
state = {
isFetching: false,
didInvalidate: false,
data: initMovies,
howMuchToLoad: 6,
},
action,
) {
switch (action.type) {
case REQUEST_MOVIES:
let howMuchToLoads;
if (state.howMuchToLoad > 20) {
howMuchToLoads = 6;
} else {
howMuchToLoads = state.howMuchToLoad;
}
if (action.loadmore !== undefined) {
howMuchToLoads = state.howMuchToLoad + 6;
}
return Object.assign({}, state, {
isFetching: true,
didInvalidate: false,
howMuchToLoad: howMuchToLoads,
});
case RECEIVE_MOVIES:
return Object.assign({}, state, {
isFetching: false,
didInvalidate: false,
data: action.movies,
lastUpdated: action.receivedAt,
});
case INFO_ABOUT_MOVIE:
return Object.assign({}, state, {
isFetching: false,
movie: action.movie,
});
case SEARCH_MOVIE:
return Object.assign({}, state, {
isFetching: false,
query: action.query,
data: action.movies,
howMuchToLoad: 200,
});
default:
return state;
}
}
export default movies;
答案 0 :(得分:1)
我认为在您的combineReducers
中,您将电影缩减器称为movies
。因此,我会进行此更改,以使您的代码顺利运行。
将此movie: {}
添加到您的reducer中的initialState并删除initMovies
,因为它是无用的代码(不应该责怪您无法使其工作,但无用的代码是无用的):
function movies(
state = {
isFetching: false,
didInvalidate: false,
data: initMovies,
howMuchToLoad: 6,
movie: {},
},
action,
) {
switch (action.type) {
因此,在您的组件中,您可以摆脱mapStateToProps
中的检查:
const mapStateToProps = (state) => {
return {
isFetching: state.movies.isFetching,
movie: state.movies.movie,
};
};
最后在你的组件中检查电影是在props
中发送的,以避免在尚未提取异步数据时出错,尽管你应该使用prop isFetching
控制它以更清晰并控制渲染的内容取决于获取状态:
const App = props =>
<div>{`Users: ${props.movie ? props.movie.id : ''}`}</div>;
我仍然建议你在componentDidMount中执行动作调用,原因在我的评论中有解释。
另一个建议是尽可能简化操作。我会对cDM中的组件进行axios调用,并将响应数据发送给操作。
// action
export const getInfoAboutMovie = movie => (dispatch) => {
dispatch(requestPopularMovies());
dispatch(infoAboutMovie(movie));
};
// component
import { API_KEY } from '../util.const';
...
componentDidMount() {
axios.get(`https://api.themoviedb.org/3/movie/211672?api_key=${API_KEY}&language=en-US`)
.then((res) => {
this.props.getInfo(res.data);
// Manage the errors properly
}).catch((err) => console.log(err));
}