【发布时间】:2019-09-29 15:27:48
【问题描述】:
React 新手,为调用 Spotify Web API 的 Codecademy 项目创建应用程序。
API 调用正在运行:我正在获取访问令牌和到期计时器,然后获取以数组形式返回的歌曲结果。然后我在 App.js 中使用setState,以便searchResults = Spotify 返回的数组。然后我将 searchResults 从 state 通过 props 传递给子组件。
App.js(状态)-> Search-container.js -> Search-view.js -> SearchResults-container.js -> SearchResults-view.js -> Track-container.js
我可以看到state 正在被props 成功传递,因为我正在将this.props.searchResults 记录到Track-container.js 中的控制台并查看结果数组。
但是,在控制台的下一行,它变成了undefined。
控制台截图:https://i.imgur.com/XkMEb4o.png
控制台:
Did update
Track-container.js:19 [{…}]
Track-container.js:18 Did update
Track-container.js:19 undefined
Track-container.js:18 Did update
Track-container.js:19 [{…}]
Track-container.js:18 Did update
Track-container.js:19 undefined
Track-container.js:18 Did update
Track-container.js:19 [{…}]
Track-container.js:18 Did update
Track-container.js:19 undefined
Track-container.js:18 Did update
Track-container.js:19 [{…}]
Track-container.js:18 Did update
Track-container.js:19 undefined
Spotify.js:44 BQBLcVOKRR7i2MjOoNu9lp4He2oOJ1FN8e90Cbben-naezHF3DP7ZWgTlCcDvIBXsa5KXQndALtkoxBtY3RYR8BhTfVnZ5QdlE-vMVQ_mgnlHqT4M_6TpLYVEisn9kw_9slvh_nPhyRIGvg7gA
Spotify.js:45 3600
Track-container.js:18 Did update
Track-container.js:19 (20) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
Track-container.js:18 Did update
Track-container.js:19 undefined
Track-container.js 中的 componentDidUpdate() 方法会在我每次在搜索字段(有一个 onChange 处理程序)中输入时记录到控制台。不确定这是否是 React 中的预期行为?
我的代码:
Spotify.js:
export class Spotify extends React.Component {
constructor(props) {
super(props);
}
getAccessToken() {
if (userAccessToken) { // If access token already defined
return userAccessToken;
} else if (window.location.href.match(userAccessTokenRegex) != null) { // If access token is not yet defined but there is an access token in the URL
// Set access token from URL hash fragment
userAccessToken = window.location.href.match(userAccessTokenRegex)[1];
// Set expiry timer from URL hash fragment
expiresIn = window.location.href.match(expiresInRegex)[1];
// Wipe the access token after expiry timer runs out
window.setTimeout(() => userAccessToken = '', expiresIn * 1000);
// Clear the parameters from the URL
window.history.pushState('Access Token', null, '/');
} else {
window.location = authUrl; // Redirect to Spotify auth
}
}
async search(term) {
if (userAccessToken === undefined) {
this.getAccessToken();
console.log(userAccessToken);
console.log(expiresIn);
}
try {
const response = await fetch('https://api.spotify.com/v1/search?type=track&q=' + term, {
method: 'GET',
headers: {'Authorization': `Bearer ${userAccessToken}`}
})
if (response.ok) {
let jsonResponse = await response.json();
let tracks = jsonResponse.tracks.items.map(track => ({
id: track.id,
name: track.name,
artist: track.artists[0].name,
album: track.album.name,
uri: track.uri
}));
return tracks;
}
}
catch(error) {
console.log(error);
}
}
};
App.js:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
'term': '',
"searchResults": [
{}
]
}
// Create Spotify
this.Spotify = new Spotify();
// Bind this
this.handleChange = this.handleChange.bind(this);
this.search = this.search.bind(this);
this.onSearch = this.onSearch.bind(this);
}
// onChange handler for child input
handleChange(e) {
const term = e.target.value; // Take value from child component input field
this.setState({ // Update state with value
term: term
});
}
// onSubmit handler for SearchBar input
onSearch(e) {
e.preventDefault();
this.search(this.state.term);
}
// Search method
async search(term) {
const results = await this.Spotify.search(term);
this.setState({
searchResults: results
});
}
render() {
return (
<div className="columns is-marginless">
<main className="column is-two-thirds has-padding-40">
<header>
<h1 className="title">Jammming</h1>
<h2 className="subtitle">Create Spotify playlists.</h2>
</header>
<Search searchResults={this.state.searchResults} onChange={this.handleChange} onSearch={this.onSearch} value={this.state.term} />
</main>
<aside className="column is-one-third is-paddingless">
<Playlist />
</aside>
</div>
);
}
}
[...4 个组件在中间,每个组件通过 props 向下传递状态...]
Track-container.js:
export class Track extends React.Component {
constructor(props) {
super(props);
}
componentDidUpdate() {
console.log('Did update');
console.log(this.props.searchResults);
}
render() {
return (
<div className="TrackList">
</div>
);
}
}
最终在 Track-container.js 中,我想映射数组以为数组中的每个项目输出一个 <TrackView /> 组件,但我还不能这样做,因为数组是 undefined。
编辑:
添加搜索组件的代码以防出现错误。
Search-container.js:
import React from 'react';
// Import child components
import { SearchView } from './Search-view';
export class Search extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<SearchView searchResults={this.props.searchResults} onChange={this.props.onChange} onSearch={this.props.onSearch} value={this.props.value} />
);
}
}
Search-view.js:
import React from 'react';
// Import child components
import { SearchBar } from './SearchBar';
import { SearchResults } from './SearchResults';
export const SearchView = (props) => {
return (
<section className="Search">
<SearchBar onChange={props.onChange} onSearch={props.onSearch} value={props.value} />
<SearchResults searchResults={props.searchResults} />
</section>
);
}
SearchBar-container.js:
import React from 'react';
import { SearchBarView } from './SearchBar-view';
import Spotify from '../../../../utils/Spotify';
export class SearchBar extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<SearchBarView onChange={this.props.onChange} onSearch={this.props.onSearch} value={this.props.value} />
<h2>{this.props.value}</h2>
</div>
);
}
}
SearchBar-view.js:
import React from 'react';
import './SearchBar.scss'; // Import styles
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faSearch } from '@fortawesome/pro-regular-svg-icons';
export const SearchBarView = (props) => {
return (
<form className="SearchBar columns is-variable is-2" onSubmit={props.onSearch}>
<div className="column">
<p className="control has-icons-left">
<input className="input is-large" placeholder="Enter a song, album, or artist" onChange={props.onChange} value={props.value} />
<span className="icon is-small is-left">
<FontAwesomeIcon icon={faSearch} />
</span>
</p>
</div>
<div className="column is-narrow">
<button className="SearchButton button is-large is-primary">Search</button>
</div>
</form>
);
}
SearchResults-container.js:
import React from 'react';
// Import components
import { SearchResultsView } from './SearchResults-view';
export class SearchResults extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<SearchResultsView searchResults={this.props.searchResults} />
);
}
}
SearchResults-view.js:
import React from 'react';
// Import components
import { Track } from '../../Track';
export const SearchResultsView = (props) => {
return (
<>
<Track searchResults={props.searchResults} />
</>
);
}
GitHub 仓库:https://github.com/edxv/jammming
【问题讨论】:
-
在 Spotify.js 中获取 jsonResponse 后返回 jsonResponse。在 app.js 中初始化状态时尝试 searchResults:[ ] 并删除 [{ }]。现在您应该看到预期的输出
-
SearchResults 组件呢?
-
既然这是一个你正在做的一个教程项目,你有没有把它发布到 github 或其他什么地方?我认为组件结构很好。你的 Spotify API 不需要是一个组件,因为你没有像使用它一样使用它。您可以像使用普通课程一样使用它。
-
请不要破坏您的帖子。这只会为我们所有人创造更多的工作。