【问题标题】:React setState working then getting undefined反应 setState 工作然后变得未定义
【发布时间】:2019-09-29 15:27:48
【问题描述】:

React 新手,为调用 Spotify Web API 的 Codecademy 项目创建应用程序。

API 调用正在运行:我正在获取访问令牌和到期计时器,然后获取以数组形式返回的歌曲结果。然后我在 App.js 中使用setState,以便searchResults = Spotify 返回的数组。然后我将 searchResultsstate 通过 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 中,我想映射数组以为数组中的每个项目输出一个 &lt;TrackView /&gt; 组件,但我还不能这样做,因为数组是 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 不需要是一个组件,因为你没有像使用它一样使用它。您可以像使用普通课程一样使用它。
  • 请不要破坏您的帖子。这只会为我们所有人创造更多的工作。

标签: reactjs state setstate


【解决方案1】:

我知道发生了什么。虽然有些东西可以改成更惯用的方式,但代码是可以工作的。

您有 Track 两个组件。一个是搜索的孩子,另一个是播放列表的孩子。播放列表中的 Track 组件不使用任何道具,因此 searchResults 未定义。 Search 中的 Track 很好,并且有一组曲目。

是您的 console.log 误导了您。这两个 componentDidUpdate 调用来自树中的两个不同节点。

继续学习本教程。 React Dev Tools 会向您展示每个组件上的 props,并且您的 Search Track 肯定有该数组。

【讨论】:

    【解决方案2】:

    嗨@edx,它对我来说很好用。也许你再试一次,让我知道。在 Spotify.js 中:

    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();
           return jsonResponse.tracks.items;
        }
        catch(error) {
          console.log(error);
        }
      }
    

    在 App.js 中:

    this.state = {
       searchResults : []
    }
    

    在轨道容器中: 我试着作为回报:

    {this.props.searchResults && this.props.searchResults.map(item => {item.album.name})}

    【讨论】:

    • 这是因为当组件渲染时结果是未定义的。它没有价值。所以你需要保护代码。如果你使用 immutable 或 loadash,它会处理这个问题。很高兴它成功了
    猜你喜欢
    • 2018-04-07
    • 1970-01-01
    • 1970-01-01
    • 2018-10-06
    • 1970-01-01
    • 2019-04-13
    • 1970-01-01
    • 1970-01-01
    • 2019-02-07
    相关资源
    最近更新 更多