Home > Software engineering >  cannot get my state variable to update properly on button click
cannot get my state variable to update properly on button click

Time:08-28

I have been trying to update my videos so that only one will be playing at a time whenever another video is clicked my state seems to be undefined after clicking the play button and i am not too sure why or if this is the best way to go about what i am trying to achieve can anyone help me out?

This is my main component:

const [allVideos, setAllVideos] = useState(initVideos(data));

 function generateVideo(data, index) {
    return {
      url: data[index].url,
      id: data[index].id,
      playing: false,
    };
  }

  function initVideos(data) {
    const videos = [];

    for (let i = 0; i < data.length; i  ) {
      videos.push(generateVideo(data, i));
    }
   
    return videos;
  }
  const videos = allVideos.map((item) => {
    return (
      <>
        <Playlist url={item.url} id={item.id} playing={item.playing} />
        <button onClick={() => playVideo(item.id)}>Play</button>
      </>
    );
  });

  function playVideo(id) {

    setAllVideos((oldVideos) => {
      oldVideos.map((video) => {
        return video.id === id
          ? { ...video, playing: !video.playing }
          : { ...video, playing: false };
      });
    });


  }
  

  return (
    <div className="sidebar-container" style={styles.container}>
      <button className="add-playlist" onClick={(props) => addPlaylist()}>
        Add New Playlist
      </button>
      <p>sidebar for now!</p>
      {videos}
    </div>
  );
}

here is my playlist component which the array is made up of:

 return (
    <div className="playlist-container">
      <Container>
        <ReactPlayer url={props.url} id={props.id} playing={props.playing} />
      </Container>

      <p>This is the playlists name {props.name}</p>
      <p>The playlist is {props.playtime} long</p>
    </div>
  );
}

can anyone suggest how to solve my issue or a better way about making sure only a single video can play at a time?

CodePudding user response:

The issue was that you were not retuning the map result in your playVideo function. You have to return the map array result as well.

Code:

 function playVideo(id) {
  
    setAllVideos((oldVideos) => {
      return oldVideos.map((video) => {
        return video.id === id
          ? { ...video, playing: !video.playing }
          : { ...video, playing: false };
      });
    });
  }

CodePudding user response:

The issue is already described but i'd recommend you to simplify your code a bit. And keep an eye on key props, anything that is inside .map should have a unique key at top level fragment. So i replaced your <> with <React.Fragment key={}...

import React, { useMemo, useState } from "react";
import ReactPlayer from "react-player";

const data = [
  { url: "https://www.youtube.com/watch?v=ysz5S6PUM-U", id: "1" },
  { url: "2", id: "2" },
  { url: "3", id: "3" },
  { url: "4", id: "4" },
  { url: "5", id: "5" }
];

export default function App() {
  const allVideos = useMemo(() => data, []);
  const [playingVideoId, setPlayingVideoId] = useState();

  const videos = allVideos.map((item) => {
    return (
      <React.Fragment key={item.id}>
        <Playlist
          url={item.url}
          id={item.id}
          playing={item.id === playingVideoId}
        />
        <button onClick={() => playVideo(item.id)}>Play</button>
      </React.Fragment>
    );
  });

  function playVideo(id) {
    setPlayingVideoId((prev) => (prev === id ? undefined : id));
  }

  function addPlaylist() {}

  return (
    <div className="sidebar-container">
      <button className="add-playlist" onClick={(props) => addPlaylist()}>
        Add New Playlist
      </button>
      <p>sidebar for now!</p>
      {videos}
    </div>
  );
}

const Playlist = (props) => {
  return (
    <div className="playlist-container">
      <div>
        <p>
          {props.url} {props.id}
        </p>
        <ReactPlayer url={props.url} id={props.id} playing={props.playing} />
      </div>

      <p>This is the playlists name {props.name}</p>
      <p>The playlist is {props.playtime} long</p>
    </div>
  );
};

Edit bold-mountain-34lsen

And a little note: You might get some issues with autoplay due to browser's policy. But after you click on play button once visited the page (you can refresh the page or come back later) - autoplay will continue to work normally.

Chrome's autoplay policies are simple: Autoplay with sound is allowed if: The user has interacted with the domain (click, tap, etc.). On desktop, the user's Media Engagement Index threshold has been crossed, meaning the user has previously played video with sound. The user has added the site to their home screen on mobile or installed the PWA on desktop. Top frames can delegate autoplay permission to their iframes to allow autoplay with sound.

Additionally: ReactPlayer repo readme has some notes about autoplay:

As of Chrome 66, videos must be muted in order to play automatically. Some players, like Facebook, cannot be unmuted until the user interacts with the video, so you may want to enable controls to allow users to unmute videos themselves. Please set muted={true}.

  • Related