Search code examples
javascriptreactjshtml5-audioreact-state

State JSX component not updating


I have a audio component that uses a file picker and on it's onChange event, the state is updated and the audio component is reloaded.

fileLoaded = (event) => {

   const url = URL.createObjectURL(event.target.files[0]);

   let react_player = (
     <AudioPlayer url={url} name={event.target.files[0].name} />
   );

   this.setState({ reactPlayer: react_player });

   event.preventDefault();
 };

render() {

   return (
     <div>
       <form onSubmit={this.submitAudioFile}>
         <Button
           className="Button"
           size="large"
           variant="contained"
           component="label"
           startIcon={<CloudUploadIcon />}
         >
           Upload File
           <input
             accept="audio/*"
             type="file"
             onChange={this.fileLoaded}
             style={{ display: "none" }}
           />
         </Button>
       </form>
       {this.state.reactPlayer}
     </div>
   );
 } 

So every time the file is changed the reactPlayer component is supposed to be re-rendered from the state. But that is not happening and the file loaded into reactPlayer remains the same even after triggering the onChange event.

This is the AudioPlayer component.

  constructor(props) {
    super();
    this.audioRef = React.createRef();
  }

  componentDidMount() {
    this.setState({ audioSource: this.props.url, name: this.props.name });
    console.log(this.props.name);
  }

  state = {
    playing: false,
    source: null,
    currentTime: 0,
    totalTime: 0,
    audioSource: "",
    name: "",
  };

  playOrPauseButton = () => {
    // console.log("CALLED");

    // const styleJson = { color: "#000", fontSize: "50" };

    if (!this.state.playing) {
      return (
        <PlayCircleFilledIcon
          //   style={styleJson}
          className="playPauseButtonHover"
          onClick={this.playOrPause}
        />
      );
    } else {
      return (
        <PauseCircleFilledIcon
          className="playPauseButtonHover"
          //   style={styleJson}
          onClick={this.playOrPause}
        />
      );
    }
  };

  playOrPause = () => {
    // console.log("Hello");
    if (this.state.playing) {
      this.audioRef.current.pause();
      console.log("pause");
    } else {
      this.audioRef.current.play();
      console.log("play");
    }
    const prevPlayingState = this.state.playing;
    this.setState({ playing: !prevPlayingState });
  };

  audioIsPlaying = (e) => {
    this.setState({ currentTime: Math.floor(e.target.currentTime) });
    this.setState({ duration: Math.floor(e.target.duration) });

    // console.log(this.state.currentTime + ":" + this.state.duration);
  };

  audioEnded = (e) => {
    console.log("ended");
    this.setState({ playing: false, currentTime: 0 });
  };

  audioWasLoaded = (e) => {
    this.setState({ duration: Math.floor(e.target.duration) });
    // let x = e.target.src;
  };

  render() {
    let x = this.playOrPauseButton();
    // this.getAudioLength();

    return (
      <div>
        <LinearProgress
          className="top"
          variant="determinate"
          value={(this.state.currentTime * 100) / this.state.duration}
        />
        <div className={styles.audioPlayerDiv}>
          {x}
          <div>
            <p style={{ textAlign: "center" }}>
              {this.state.currentTime}/{this.state.duration}
            </p>

            <p style={{ fontWeight: "bold" }}>{this.state.name}</p>
          </div>

          <audio
            onLoadedMetadata={(event) => this.audioWasLoaded(event)}
            onEnded={(event) => this.audioEnded(event)}
            onTimeUpdate={(event) => this.audioIsPlaying(event)}
            src={this.state.audioSource}
            ref={this.audioRef}
          />

          <NoteAddIcon className="playPauseButtonHover"></NoteAddIcon>
          {/* <button style={{ alignItems: "left" }}>take note</button> */}
        </div>
      </div>
    );
  }
}

export default AudioPlayer;```

Solution

  • Don't store jsx in state

    Just store the url an name in state and render jsx.

    Like this

    ...
    state = {
        url: '',
        name: ''
    }
    fileLoaded = (event) => {
    
        const url = URL.createObjectURL(event.target.files[0]);
     
        let react_player = {
            url,
            name: event.target.files[0].name
        };
     
        this.setState({ reactPlayer: react_player });
     
        event.preventDefault();
      };
     
     render() {
     
        return (
          <div>
            <form onSubmit={this.submitAudioFile}>
              <Button
                className="Button"
                size="large"
                variant="contained"
                component="label"
                startIcon={<CloudUploadIcon />}
              >
                Upload File
                <input
                  accept="audio/*"
                  type="file"
                  onChange={this.fileLoaded}
                  style={{ display: "none" }}
                />
              </Button>
            </form>
            {/* {this.state.reactPlayer} */}
            {this.state.url && <AudioPlayer url={this.state.url} name={this.state.name} />}
          </div>
        );
      }