ReactJS - 按钮不会在状态更改时重新呈现?

IT技术 javascript reactjs
2021-05-11 06:45:18

在这里react新手,

import React, { Component } from "react";

class AudioList extends Component {
  constructor(props) {
    super(props);

    this.audios = [];
    this.buttonText = [];
    for (let i = 0; i < this.props.songs.length; i++) {
      this.audios.push(new Audio(this.props.songs[i].song_url));
      this.buttonText.push(String(i));
    }

    this.state = {
      songs: "",
      buttonText: this.buttonText
    };
  }

  componentWillMount() {
    const songs = [];
    for (let i = 0; i < this.props.songs.length; i++) {
      this.audios[i].addEventListener("play", () => {
        let stateArray = [...this.state.buttonText];
        let stateArrayElement = { ...stateArray[i] };
        stateArrayElement = "playing";
        stateArray[i] = stateArrayElement;
        console.log(stateArray);
        this.setState({ buttonText: stateArray });
        console.log(this.state.buttonText[i]);
      });
      songs.push(
        <div className="song-preview">
          <button
            className="preview"
            onClick={() => this.toggle(this.audios[i])}
          >
            {this.state.buttonText[i]}
          </button>
        </div>
      );
    }

    this.setState({
      songs: songs
    });
  }

  componentWillUnmount() {
    for (let i = 0; i < this.props.songs.length; i++) {
      this.audios[i].pause();
    }
  }

  getCurrentAudio() {
    return this.audios.find(audio => false === audio.paused);
  }

  toggle(nextAudio) {
    const currentAudio = this.getCurrentAudio();

    if (currentAudio && currentAudio !== nextAudio) {
      currentAudio.pause();
      nextAudio.play();
    }

    nextAudio.paused ? nextAudio.play() : nextAudio.pause();
  }

  render() {
    if (this.state.songs) {
      return <div className="song-list">{this.state.songs}</div>;
    } else {
      return <div className="song-list"></div>;
    }
  }
}

export default AudioList;

我正在使用我在 Stackoverflow ( https://stackoverflow.com/a/50595639 )上找到的先前解决方案中的此代码我能够实施此解决方案来解决我自己的挑战,即需要使用一个音频播放器和多个按钮拥有多个音频源。但是,我现在面临一个新的挑战 - 我希望在触发事件时更改特定按钮的文本。

我想出了这个实现,其中按钮文本基于名为 buttonText 的状态中的数组。按钮在启动时正确呈现,但是当事件侦听器拾取事件并更改状态时,按钮中的文本不会重新呈现或更改,即使它基于处于状态的数组中的元素在改变。

有没有人对为什么它可能无法重新渲染有任何建议?

编辑:更改状态中的单个数组元素基于React:how to update state.item[1] in state using setState?

2个回答

我对您的代码进行了一些重组但它未经测试,现在已经过测试)

const songs = [
  { 
    title: "small airplane long flyby - Mike_Koenig",
    song_url: "http://soundbible.com/mp3/small_airplane_long_flyby-Mike_Koenig-806755389.mp3" 
  },
  { 
    title: "Female Counts To Ten",
    song_url: "http://soundbible.com/mp3/Female%20Counts%20To%20Ten-SoundBible.com-1947090250.mp3" 
  },
];

class AudioList extends React.Component {
  audios = new Map();

  state = {
    audio: null,
    song: null
  };

  componentDidUpdate() {
    // stop playing if the song has been removed from props.songs
    if (this.state.song && !this.props.songs.some(song => song.song_url === this.state.song.song_url)) {
      this.toggle(this.state.audio, this.state.song);
    }
  }

  componentWillUnmount() {
    if (this.state.audio) {
      this.state.audio.pause();
    }
    this.audios.clear();
  }

  toggle(audio, song) {
    this.setState(state => {
      if (audio !== state.audio) {
        if (state.audio) {
          state.audio.pause();
        }
        audio.play();

        return { audio, song };
      }

      audio.pause();
      return { audio: null, song: null };
    });
  }

  getAudio(song) {
    let audio = this.audios.get(song.song_url);
    if (!audio) {
      this.audios.set(song.song_url, audio = new Audio(song.song_url));
    }
    return audio;
  }

  render() {
    return <div className="song-list">{
      this.props.songs.map((song, i) => {
        const audio = this.getAudio(song);
        const playing = audio === this.state.audio;

        return <div className="song-preview">
          <button
            className="preview"
            onClick={this.toggle.bind(this, audio, song)}
          >
            {playing ? "playing" : (song.title || i)}
          </button>
        </div>
      })
    }</div>;
  }
}

ReactDOM.render(<AudioList songs={songs} />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

编辑:将 a 添加title到歌曲对象并将它们显示在按钮上

我只是将所有代码从 componentWillMount() 移到了 render()。我还将“歌曲”作为状态变量删除,并将其设置为仅存在于渲染中的变量,因为歌曲只是一组 div。

import React, { Component } from "react";

const audio1 =
  "http://soundbible.com/mp3/small_airplane_long_flyby-Mike_Koenig-806755389.mp3";
const audio2 =
  "http://soundbible.com/mp3/Female%20Counts%20To%20Ten-SoundBible.com-1947090250.mp3";

class AudioList extends Component {
  constructor(props) {
    super(props);

    this.audios = [];
    this.buttonText = [];
    for (let i = 0; i < this.props.songs.length; i++) {
      this.audios.push(new Audio(this.props.songs[i]));
      this.buttonText.push(String(i));
    }

    this.state = {
      buttonText: this.buttonText
    };
  }

  componentWillUnmount() {
    for (let i = 0; i < this.props.songs.length; i++) {
      this.audios[i].pause();
    }
  }

  getCurrentAudio() {
    return this.audios.find(audio => false === audio.paused);
  }

  toggle(nextAudio) {
    const currentAudio = this.getCurrentAudio();

    if (currentAudio && currentAudio !== nextAudio) {
      currentAudio.pause();
      nextAudio.play();
    }

    nextAudio.paused ? nextAudio.play() : nextAudio.pause();
  }

  render() {
    const songs = [];
    for (let i = 0; i < this.props.songs.length; i++) {
      this.audios[i].addEventListener("play", () => {
        console.log("playing");
        let stateArray = [...this.state.buttonText];
        let stateArrayElement = { ...stateArray[i] };
        stateArrayElement = "playing";
        stateArray[i] = stateArrayElement;
        console.log(stateArray);
        this.setState({ buttonText: stateArray });
        console.log(this.state.buttonText);
      });
      songs.push(
        <div className="song-preview">
          <button
            className="preview"
            onClick={() => this.toggle(this.audios[i])}
          >
            {this.state.buttonText[i]}
          </button>
        </div>
      );
    }

    return (
        <div>{songs}</div>
    )
  }
}

export default () => <AudioList songs={[audio1, audio2]} />;

代码现在按预期运行。