benwiley4000 / cassette

📼 A flexible media player component library for React that requires no up-front config
https://benwiley4000.github.io/cassette/styleguide
MIT License
183 stars 28 forks source link

Setting index in PlayerContextProvider outside #480

Open steveambielli opened 2 years ago

steveambielli commented 2 years ago

In nextjs I have a page that displays the track list. Cassette is loaded in _app.tsx. How can I set the active index of the current playlist from page/[playlist].tsx? I tried using the hooks npm but I receive a fatal error.

benwiley4000 commented 2 years ago

Could you please share the fatal error message you received and share the relevant code you wrote in app.tsx and playlist.tsx ?

Le lun. 7 mars 2022 10 h 34 a.m., Steve @.***> a écrit :

In nextjs I have a page that displays the track list. Cassette is loaded in _app.tsx. How can I set the active index of the current playlist from page/[playlist].tsx? I tried using the hooks npm but I receive a fatal error.

— Reply to this email directly, view it on GitHub https://github.com/benwiley4000/cassette/issues/480, or unsubscribe https://github.com/notifications/unsubscribe-auth/ADHOD3JHSNAVPBXVS6OLDQDU6YOWRANCNFSM5QDUPXNA . You are receiving this because you are subscribed to this thread.Message ID: @.***>

steveambielli commented 2 years ago

Thanks for the help and quick reply!

This is without the hooks package.

_app.tsx

<PlayerContextProvider
            playlist={playlistTracks}
            onActiveTrackUpdate={(e) => console.log(e)}
            mediaElementRef={
              elem => {
                elem.addEventListener('timeupdate', (e) => console.log(e));
                elem.addEventListener('volumechange', (e) => console.log(e));
                elem.addEventListener('loadstart', (e) => console.log(e));
              }
            }
          >
            <MediaPlayerControls
              controls={[
                'backskip',
                'playpause',
                'forwardskip',
                'shuffle',
                'repeat',
                'volume',
                'progress'
              ]}
            />
          </PlayerContextProvider>

pages/playlist/[playlist].tsx

const handleListItemClick = (
    index: number,
    event: React.MouseEvent<HTMLDivElement, MouseEvent>,
    id: number,
    title: string,
    url: string
  ) => {
    setSelectedTrackIndex(index)
    setTrackIndex(index)
    //TODO: play song in cassette
  }

<List>
            {playlistTracks.map((track:any, index:number) => (
              <ListItem key={track.id} disablePadding>
                <ListItemButton
                  selected={selectedTrackIndex === index}
                  onClick={(event) => handleListItemClick(index, event, track.id, track.title, track.url)}
                >
                  {track.title}
                </ListItemButton>
              </ListItem>
            ))}
</List>

This is when using hooks. pages/playlist/[playlist].tsx

import { useState } from 'react';
import Box from '@mui/material/Box'
import Container from '@mui/material/Container'
import List from '@mui/material/List'
import ListItem from '@mui/material/ListItem'
import ListItemButton from '@mui/material/ListItemButton'
import { usePlayerContext } from '@cassette/hooks'

const Playlist = ({playlistName, playlistTracks, setTrackIndex} : {playlistName:any, playlistTracks:any, setTrackIndex:any}) => {
  const [selectedTrackIndex, setSelectedTrackIndex] = useState(0)

  const handleListItemClick = (
    index: number,
    event: React.MouseEvent<HTMLDivElement, MouseEvent>,
    id: number,
    title: string,
    url: string
  ) => {
    setSelectedTrackIndex(index)
    //TODO: play song in cassette
    setTrackIndex(index)
  }

  function PlayerContextUser() {
    const { currentTime, paused, onTogglePause } = usePlayerContext([
      'currentTime',
      'paused',
      'onTogglePause'
    ]);

    return (
      <>
        <div>
          The track is {paused ? 'paused' : 'playing'}. The time is {currentTime}.
        </div>
        <button onClick={onTogglePause}>Toggle pause!</button>
      </>
    );
  }

  return (
    <>
      <Box
        component="main"
        sx={{
          backgroundColor: (theme) =>
            theme.palette.mode === 'light'
            ? theme.palette.grey[100]
            : theme.palette.grey[900],
          flexGrow: 1,
          height: '100vh',
          overflow: 'auto',
        }}
      >
        <Container
          maxWidth={false}
          sx={{ mt: 10 }}
        >
          <h3>{playlistName}</h3>
          <PlayerContextUser />
          <List>
            {playlistTracks.map((track:any, index:number) => (
              <ListItem key={track.id} disablePadding>
                <ListItemButton
                  selected={selectedTrackIndex === index}
                  onClick={(event) => handleListItemClick(index, event, track.id, track.title, track.url)}
                >
                  {track.title}
                </ListItemButton>
              </ListItem>
            ))}
          </List>
        </Container>
      </Box>
    </>
  )
}

export default Playlist

On the frontend I get this error.

Unhandled Runtime Error
TypeError: Cannot read properties of null (reading 'hasOwnProperty')

pages\playlist[playlist].tsx (25:68) @ PlayerContextUser

function PlayerContextUser() {
> 25 |     const { currentTime, paused, onTogglePause } = usePlayerContext([
     |                                                                    ^
  26 |       'currentTime',
  27 |       'paused',
  28 |       'onTogglePause'
benwiley4000 commented 2 years ago

At a glance, it seems like you might not be rendering the PlayerContextProvider high enough in your app. It doesn't need to be the immediate parent of the MediaPlayerControls but it definitely does need to be an ancestor of any part of the app that uses the playercontext. Is it possible your list component does not have the PlayerContextProvider ancestor? Otherwise we don't know what player context you're referring to.

Not sure if you're used to redux but it's kind of like the redux provider. Usually you'd put it at the very top of your app, but if you have multiple media players on the same page, then you would put it lower.

Does this help?

Le lun. 7 mars 2022 11 h 07 a.m., Steve @.***> a écrit :

Thanks for the help and quick reply!

This is without the hooks package.

_app.tsx

<PlayerContextProvider playlist={playlistTracks} onActiveTrackUpdate={(e) => console.log(e)} mediaElementRef={ elem => { elem.addEventListener('timeupdate', (e) => console.log(e)); elem.addEventListener('volumechange', (e) => console.log(e)); elem.addEventListener('loadstart', (e) => console.log(e)); } }

<MediaPlayerControls controls={[ 'backskip', 'playpause', 'forwardskip', 'shuffle', 'repeat', 'volume', 'progress' ]} />

pages/playlist/[playlist].tsx

const handleListItemClick = ( index: number, event: React.MouseEvent<HTMLDivElement, MouseEvent>, id: number, title: string, url: string ) => { setSelectedTrackIndex(index) setTrackIndex(index) //TODO: play song in cassette }

{playlistTracks.map((track:any, index:number) => ( handleListItemClick(index, event, track.id, track.title, track.url)} > {track.title} ))} This is when using hooks. pages/playlist/[playlist].tsx import { useState } from 'react'; import Box from ***@***.***/material/Box' import Container from ***@***.***/material/Container' import List from ***@***.***/material/List' import ListItem from ***@***.***/material/ListItem' import ListItemButton from ***@***.***/material/ListItemButton' import { usePlayerContext } from ***@***.***/hooks' const Playlist = ({playlistName, playlistTracks, setTrackIndex} : {playlistName:any, playlistTracks:any, setTrackIndex:any}) => { const [selectedTrackIndex, setSelectedTrackIndex] = useState(0) const handleListItemClick = ( index: number, event: React.MouseEvent, id: number, title: string, url: string ) => { setSelectedTrackIndex(index) //TODO: play song in cassette setTrackIndex(index) } function PlayerContextUser() { const { currentTime, paused, onTogglePause } = usePlayerContext([ 'currentTime', 'paused', 'onTogglePause' ]); return ( <>
The track is {paused ? 'paused' : 'playing'}. The time is {currentTime}.
); } return ( <> theme.palette.mode === 'light' ? theme.palette.grey[100] : theme.palette.grey[900], flexGrow: 1, height: '100vh', overflow: 'auto', }} >

{playlistName}

{playlistTracks.map((track:any, index:number) => ( handleListItemClick(index, event, track.id, track.title, track.url)} > {track.title} ))}
) } export default Playlist On the frontend I get this error. Unhandled Runtime Error TypeError: Cannot read properties of null (reading 'hasOwnProperty') pages\playlist[playlist].tsx (25:68) @ PlayerContextUser function PlayerContextUser() { > 25 | const { currentTime, paused, onTogglePause } = usePlayerContext([ | ^ 26 | 'currentTime', 27 | 'paused', 28 | 'onTogglePause' — Reply to this email directly, view it on GitHub , or unsubscribe . You are receiving this because you commented.Message ID: ***@***.***>
benwiley4000 commented 2 years ago

In case you're still stuck, it might help if you can share the full code of your app.tsx component. Let me know.

Le lun. 7 mars 2022 11 h 14 a.m., Ben Wiley @.***> a écrit :

At a glance, it seems like you might not be rendering the PlayerContextProvider high enough in your app. It doesn't need to be the immediate parent of the MediaPlayerControls but it definitely does need to be an ancestor of any part of the app that uses the playercontext. Is it possible your list component does not have the PlayerContextProvider ancestor? Otherwise we don't know what player context you're referring to.

Not sure if you're used to redux but it's kind of like the redux provider. Usually you'd put it at the very top of your app, but if you have multiple media players on the same page, then you would put it lower.

Does this help?

Le lun. 7 mars 2022 11 h 07 a.m., Steve @.***> a écrit :

Thanks for the help and quick reply!

This is without the hooks package.

_app.tsx

<PlayerContextProvider playlist={playlistTracks} onActiveTrackUpdate={(e) => console.log(e)} mediaElementRef={ elem => { elem.addEventListener('timeupdate', (e) => console.log(e)); elem.addEventListener('volumechange', (e) => console.log(e)); elem.addEventListener('loadstart', (e) => console.log(e)); } }

<MediaPlayerControls controls={[ 'backskip', 'playpause', 'forwardskip', 'shuffle', 'repeat', 'volume', 'progress' ]} />

pages/playlist/[playlist].tsx

const handleListItemClick = ( index: number, event: React.MouseEvent<HTMLDivElement, MouseEvent>, id: number, title: string, url: string ) => { setSelectedTrackIndex(index) setTrackIndex(index) //TODO: play song in cassette }

{playlistTracks.map((track:any, index:number) => ( handleListItemClick(index, event, track.id, track.title, track.url)} > {track.title} ))} This is when using hooks. pages/playlist/[playlist].tsx import { useState } from 'react'; import Box from ***@***.***/material/Box' import Container from ***@***.***/material/Container' import List from ***@***.***/material/List' import ListItem from ***@***.***/material/ListItem' import ListItemButton from ***@***.***/material/ListItemButton' import { usePlayerContext } from ***@***.***/hooks' const Playlist = ({playlistName, playlistTracks, setTrackIndex} : {playlistName:any, playlistTracks:any, setTrackIndex:any}) => { const [selectedTrackIndex, setSelectedTrackIndex] = useState(0) const handleListItemClick = ( index: number, event: React.MouseEvent, id: number, title: string, url: string ) => { setSelectedTrackIndex(index) //TODO: play song in cassette setTrackIndex(index) } function PlayerContextUser() { const { currentTime, paused, onTogglePause } = usePlayerContext([ 'currentTime', 'paused', 'onTogglePause' ]); return ( <>
The track is {paused ? 'paused' : 'playing'}. The time is {currentTime}.
); } return ( <> theme.palette.mode === 'light' ? theme.palette.grey[100] : theme.palette.grey[900], flexGrow: 1, height: '100vh', overflow: 'auto', }} >

{playlistName}

{playlistTracks.map((track:any, index:number) => ( handleListItemClick(index, event, track.id, track.title, track.url)} > {track.title} ))}
) } export default Playlist On the frontend I get this error. Unhandled Runtime Error TypeError: Cannot read properties of null (reading 'hasOwnProperty') pages\playlist[playlist].tsx (25:68) @ PlayerContextUser function PlayerContextUser() { > 25 | const { currentTime, paused, onTogglePause } = usePlayerContext([ | ^ 26 | 'currentTime', 27 | 'paused', 28 | 'onTogglePause' — Reply to this email directly, view it on GitHub , or unsubscribe . You are receiving this because you commented.Message ID: ***@***.***>
steveambielli commented 2 years ago

Well, that worked! I just moved it up higher in _app.tsx. Thanks for much!

benwiley4000 commented 2 years ago

Awesome, glad that worked.

It's important to understand that Cassette doesn't use a global communication channel, it uses React Context. This means that the player context is inherited implicitly via the React hierarchy. If the component that wants to use the player context doesn't live underneath a PlayerContextProvider, it won't work.

Don't hesitate to ask more questions!

Le lun. 7 mars 2022 11 h 57 a.m., Steve @.***> a écrit :

Well, that worked! I just moved it up higher in _app.tsx. Thanks for much!

— Reply to this email directly, view it on GitHub https://github.com/benwiley4000/cassette/issues/480#issuecomment-1060907408, or unsubscribe https://github.com/notifications/unsubscribe-auth/ADHOD3J7CFVWFAWDF5Y5DC3U6YYO7ANCNFSM5QDUPXNA . You are receiving this because you commented.Message ID: @.***>