如何将react-redux与HOC组件一起使用以等待数据获取?

时间:2018-02-11 13:08:17

标签: reactjs redux react-router react-redux

这是我的组件,我想从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;

1 个答案:

答案 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));

    }