Search code examples
javascriptreactjssocket.ioreact-hooksreact-component

Can't update React Component state when triggered from outside


I'm relatively new to React and haven't found a solution online. My code:

const socketPlay = () => {
  socket.emit("playing", true);
}

const socketStop = () => {
  socket.emit("playing", false);
}

class App extends Component {
  state = {
    playing: false,
    url: 'XXXX',
  }
  
  handlePlay = () => {
    this.setState({ playing: true });
    socketPlay();
  }

  handlePause = () => {
    this.setState({ playing: false })
    socketStop();
  }

  ref = player => {
    this.player = player
  }

  render() {
    const { url, playing, controls, light, volume, muted, loop, played, loaded, duration, playbackRate, pip } = this.state
    const SEPARATOR = ' · '
    return (
      <ReactPlayer
        ref={this.ref}
        className='react-player'
        width='40%'
        playing={playing}
        onPlay={this.handlePlay}     
        onPause={this.handlePause}
      />
    )
  }
}
export default App;

const app = new App();

socket.on("toClient", (args) => {
  console.log("from server: " + args);
  switch (args) {
    case true:
      app.handlePlay();
      break;
    case false:
      app.handlePause();
      break;
    default:
      break;
  }
});

When I interact with the component via the browser handlePlay() sets the state "playing: true" correctly. If it gets called outside the component via app.handlePlay() the state remains "playing: false".

app.handlePlay() gets executed correctly. Only this.setState isn't working. The browsers console throws "Warning: Can't call setState on a component that is not yet mounted."

Help is much appreciated. Thanks in advance, Jan


Solution

  • Looking at your code, it seems to me that it would be easier to add the socket.on("toClient", listener inside the App Class with componentDidMount

    class App extends Component {
      state = {
        playing: false,
        url: 'XXXX',
      }
      componentDidMount(){ 
        socket.on("toClient", (args) => {
        console.log("from server: " + args);
        switch (args) {
        case true:
          this.handlePlay();
          break;
        case false:
          this.handlePause();
          break;
        default:
          break;
      }
    });
    }
    
      
      handlePlay = () => {
        this.setState({ playing: true });
        socketPlay();
      }
    
      handlePause = () => {
        this.setState({ playing: false })
        socketStop();
      }
    
      ref = player => {
        this.player = player
      }
    
      render() {
        const { url, playing, controls, light, volume, muted, loop, played, loaded, duration, playbackRate, pip } = this.state
        const SEPARATOR = ' · '
        return (
          <ReactPlayer
            ref={this.ref}
            className='react-player'
            width='40%'
            playing={playing}
            onPlay={this.handlePlay}     
            onPause={this.handlePause}
          />
        )
      }
    }
    export default App;