ajax - 反应 MovieDB API 问题。设置 this.setState 两次会破坏我的组件
问题描述
我遵循了一个使用带有 React 的 MovieDB API 的 youtube 教程。我可以搜索电影并提取标题、描述和电影图像。我想添加播放 youtube 预告片的功能。我已成功获取 youtube ID 并存储它们。我无法显示它们。如果我在 App.js 中取消注释
this.setState({ rows: videoRows });
然后 youtube 视频工作。但是我的标题、描述和电影图像是未定义的,反之亦然。
应用程序.js
import React, { Component } from "react";
import "./App.css";
import MovieRow from "./MovieRow.js";
import $ from "jquery";
class App extends Component {
constructor(props) {
super(props);
this.state = {};
this.performSearch("woman");
}
performSearch(searchTerm) {
// console.log("perform search");
const API_KEY = "625914798003ef54176364f32c232968";
const urlString = `https://api.themoviedb.org/3/search/movie?api_key=${API_KEY}&query=${searchTerm}`;
const urlVideoString = `https://api.themoviedb.org/3/movie/297762/videos?api_key=${API_KEY}&language=en-US`;
//fetch generic info
$.ajax({
url: urlString,
success: searchResults => {
console.log("fetch basic success");
const results = searchResults.results;
var videoRows = [];
var movieRows = [];
// call next ajax function
$.ajax({
url: urlVideoString,
success: searchResults => {
console.log("fetch Youtube video key success");
const results = searchResults.results;
results.forEach(movie => {
movie.video_src = movie.key;
console.log(movie.video_src);
var videoRow = <MovieRow key={movie.id} movie={movie} />;
videoRows.push(videoRow);
});
//If I run this line below it will break
//my generic basic info(title, movie description, and picture) ,
//but it makes the youtube player work
// this.setState({ rows: videoRows });
},
error: (xhr, status, err) => {
console.log("failed video fetch");
}
});
results.forEach(movie => {
movie.poster_src =
"https://image.tmdb.org/t/p/w185" + movie.poster_path;
console.log(movie.poster_path);
const movieRow = <MovieRow key={movie.id} movie={movie} />;
movieRows.push(movieRow);
});
this.setState({ rows: movieRows });
},
error: (xhr, status, err) => {
console.log("failed fetch");
}
});
}
searchChangeHandler(event) {
console.log(event.target.value);
const boundObject = this;
const searchTerm = event.target.value;
boundObject.performSearch(searchTerm);
}
render() {
return (
<div>
<table className="titleBar">
<tbody>
<tr>
<td>
<img alt="app icon" width="100" src="green_app_icon.svg" />
</td>
<td width="8" />
<td>
<h1>MoviesDB Search</h1>
</td>
</tr>
</tbody>
</table>
<input
style={{
fontSize: 24,
display: "block",
width: "99%",
paddingTop: 8,
paddingBottom: 8,
paddingLeft: 16
}}
onChange={this.searchChangeHandler.bind(this)}
placeholder="Search for movie by title..."
/>
{this.state.rows}
</div>
);
}
}
export default App;
电影行.js
import React from "react";
import YouTube from "react-youtube";
class MovieRow extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
viewMovie() {
const url = "https://www.themoviedb.org/movie/" + this.props.movie.id;
window.location.href = url;
}
viewTrailer() {
//const trailerURL = "https://www.youtube.com/watch?v=1Q8fG0TtVAY";
}
_onReady(event) {
// access to player in all event handlers via event.target
event.target.pauseVideo();
}
render() {
const opts = {
height: "390",
width: "50%",
playerVars: {
// https://developers.google.com/youtube/player_parameters
autoplay: 1
}
};
return (
<table key={this.props.movie.id}>
<tbody>
<tr>
<td>
<img alt="poster" width="180" src={this.props.movie.poster_src} />
</td>
<td>
<h1>src {this.props.movie.video_src}</h1>
<h3>{this.props.movie.title}</h3>
<p>{this.props.movie.overview}</p>
<YouTube
videoId={this.props.movie.video_src}
opts={opts}
onReady={this._onReady}
/>
<input
className="btn btn-primary"
type="button"
onClick={this.viewTrailer.bind(this)}
value="Play Trailer"
/>
<input
className="btn btn-primary"
type="button"
onClick={this.viewMovie.bind(this)}
value="View"
/>
</td>
</tr>
</tbody>
</table>
);
}
}
export default MovieRow;
解决方案
我看到这里发生了很多问题。一是行状态未初始化。您应该始终在构造函数上初始化一个状态
this.state = {
row: []
};
我看到的另一个问题是您不应该将组件存储在状态中。看到这个
状态应包含组件的事件处理程序可能更改以触发 UI 更新的数据。在实际应用程序中,这些数据往往非常小并且可以进行 JSON 序列化。在构建有状态组件时,请考虑其状态的最小可能表示形式,并且仅将这些属性存储在 this.state 中。在 render() 内部,只需根据此状态计算您需要的任何其他信息。你会发现以这种方式思考和编写应用程序往往会导致最正确的应用程序,因为向状态添加冗余或计算值意味着你需要显式地保持它们同步,而不是依赖 React 为你计算它们。
你可以做的是重写你的状态,比如
this.state = {
movies: []
}
在你的 ajax 请求中
// Change these lines to
results.forEach(movie => {
movie.video_src = movie.key;
console.log(movie.video_src);
var videoRow = <MovieRow key={movie.id} movie={movie} />;
videoRows.push(videoRow);
});
// To this
results.forEach(movie => {
movie.video_src = movie.key;
videoRows.push(movie);
});
this.setState({ movies: videoRows });
最后在您的渲染方法中,循环您状态中的所有电影并返回您的 MovieRow 组件
{this.state.movies.map(movie => <MovieRow key={movie.id} movie={movie}/>)}
推荐阅读
- react-testing-library - react-testing-library 中的选择器,可以通过 getAllByText 检索多个匹配项
- c++ - 有哪些足够好的方法可以将对象(数据包)插入二叉搜索树?
- reactjs - 使用 Apache 时,React 应用程序显示“404 找不到请求的路径”页面
- r - 如何修复 S-plus 中的线性模型拟合误差
- excel - 如何将变量从函数传递给子函数?
- typescript - 'as const' 与类型相结合?
- excel - 数据验证不会保存
- javascript - 如何正确利用 `.reduce` 来计算数组中的字符总数?
- arrays - AngularJS ng-repeat 和 indexOf() 检查对象
- java - 不明白这些位运算符如何对字节和整数进行操作