0

I'm using React + Rails, AWS S3 for a youtube copy. I'm having trouble hitting a link (created from react-router) for a video within a watch page. For example, I'll be in /watch/4 viewing content, but then when I hit a link for content at /watch/19, the url will change to /watch/19 but nothing else will change and the content I was watching will remain playing.

I have a VideoContainer through which I'm displaying another component VideoPlayer which holds the html video tag. I'm fetching my content at the VideoContainer level and passing down the url for my content to the VideoPlayer. The content I'm fetching is based on the content Id, which I'm deriving from App's props

App Level:

<Switch>
  <Route path="/watch/:videoID" render={() => <VideoContainer props={this.props} user={this.state.user} getUser={this.getUser} />} />
</Switch>


export default withRouter(App);

VideoContainer Level:

import React from 'react'
import VideoPlayer from '../components/VideoPlayer'
import { Grid, Card } from 'semantic-ui-react'
import NavBar from './NavBar'
import ContentCard from '../components/ContentCard'

export default class VideoContainer extends React.Component {
  constructor(props) {
    super(props)
    let contentID = this.props.props.location.pathname.split('/')[2]
    this.state = {
      contentID: contentID,
      url: '',
      name: '',
      uploader: '',
      favorite: '',
      favoriteID: '',
      user: this.props.user,
      sideBarContent: []
    }
  }

  checkIfFavorite = (id) => {
    if (this.props.user) {
      let favorite = this.props.user.favorites.filter(favorite => {
        return favorite.content_id === parseInt(id)
      })
      if (favorite.length === 1) {
        return true;
      } else {
        return false;
      }
    }
  }

  fetchContent(id) {
    fetch(`http://localhost:3000/api/v1/content/${id}`, {
      method: 'GET',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
      }
    })
    .then(res => res.json())
    .then(json => {
      if (json.content !== null) {
      this.setState({url: json.content.url, name: json.content.name, uploader: json.content.user.e_mail})
      }
    })
  }

  deleteFavorite = e => {
    let contentID = this.props.props.location.pathname.split('/')[2]
    let favorite = this.props.user.favorites.filter(favorite => {
      return favorite.content_id === parseInt(contentID)
    })[0]
    let data = {
      favorite: {
        favorite_id: favorite.id
      }
    }
    fetch(`http://localhost:3000/api/v1/favorites/${favorite.id}`, {
      method: 'DELETE',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(data)
    })
    .then(res => res.json())
    .then(json => {
      this.props.getUser()
    })
  }

  addFavorite = e => {
    let data = {
      favorite: {
      content_id: this.state.contentID,
      user_id: this.props.user.user.id,
      name: this.state.name
      }
    }
    fetch('http://localhost:3000/api/v1/favorites', {
      method: 'POST',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(data)
    })
    .then(res => res.json())
    .then(json => {
      this.props.getUser()
    })

  }

  fetchSidebarContent = () => {
    fetch('http://localhost:3000/api/v1/content', {
      method: 'GET',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
      }
    })
    .then(res => res.json())
    .then(json => {
      let videosToPick = this.randomIntsFromRange(0, json.length-1, json)
      let content = []
      videosToPick.forEach(video => content.push(json[video]))
      this.setState({sideBarContent: content})
      console.log(this.state.sideBarContent)
    })
  }

  randomIntsFromRange = (min, max, json) => {
    let randomInts = []
    let num;
    while (randomInts.length < 6) {
      num = Math.floor(Math.random() * (max-min+1)+min);
      if (!randomInts.includes(num) && json[num].name !== this.state.name) {
        randomInts.push(num)
      }
    }
    return randomInts
  }


  componentDidMount() {
    let contentID = this.props.props.location.pathname.split('/')[2]
    if (this.props.user === null) {
      this.props.getUser()
      .then(this.fetchContent(contentID))
      .then(this.fetchSidebarContent())
    }
  }

  componentDidUpdate(prevProps, prevState) {
    let contentID = this.props.props.location.pathname.split('/')[2]
    if (contentID !== prevState.contentID) {
      this.props.getUser()
      .then(this.fetchContent(contentID))
      .then(this.fetchSidebarContent())
      .then(this.setState({contentID: contentID}))
    }
  }

  render() {
    console.log(this.state.favorite)
    let contentID = this.props.props.location.pathname.split('/')[2]
    let contentArr = [];
    if (this.state.sideBarContent.length > 0) {
      this.state.sideBarContent.forEach(content => {
        contentArr.push(<ContentCard key={content.id} content={content} />)
      })
    } else {
        this.fetchSidebarContent()
    }

    return(
      <Grid padded container style={{height: '100vh', width: '100vw'}}>
        <Grid.Row stretched style={{height: '100%'}}>
          <Grid.Column textAlign='center' width={2}>
            <NavBar />
          </Grid.Column>
          <Grid.Column textAlign='left' width={10}>
            <Grid.Row style={{height: '4%'}}>
            </Grid.Row>
            <Grid.Row stretched style={{height: '96%'}}>
              {this.state.url ? (
                <>
                  <VideoPlayer src={this.state.url} />
                  <h2>{this.state.name}</h2>
                  <h4>Uploaded by: {this.state.uploader}</h4>
                  {this.checkIfFavorite(this.props.props.location.pathname.split('/')[2]) ? (<button onClick={this.deleteFavorite}>Unfavorite</button>) : (<button onClick={this.addFavorite}>Favorite</button>) }
                </>) : (
                this.fetchContent(contentID)
              )}
            </Grid.Row>
          </Grid.Column>
          <Grid.Column></Grid.Column>
          <Grid.Column stretched textAlign='left' width={3}>
            <Grid.Row stretched style={{height: '4%'}}>
            </Grid.Row>
            <Grid.Row stretched style={{height: '96%'}}>
              <h3>Next video up:</h3>
              {this.state.sideBarContent.length > 0 ? (
                <Card.Group fluid itemsPerRow={1}>
                  {contentArr}
                </Card.Group>
                ) : (this.fetchSidebarContent())}
            </Grid.Row>
          </Grid.Column>
        </Grid.Row>
      </Grid>

      )
  }
}

VideoPlayer component:

import React from 'react'

export default class VideoPlayer extends React.Component {

  render() {
    return(
      <div>
        <video width="720" height="480" controls controlsList="nodownload" autoPlay>
          <source src={this.props.src} type="video/mp4" />
          Your browser does not support the video tag
        </video>
      </div>
      )
  }
}

Please let me know if there's any additional code you might need to see.

Hassan
  • 29
  • 3

1 Answers1

0

The reason the video is not switched is because the video's data is fetched on componentDidMount. This lyfecycle hook is executed only once in the component lifetime when the component is mounted. To detect a change in its props, you should use componentDidUpdate like this:

componentDidUpdate(prevState, prevState) {
  // Detect if the video id has changed
  const contentID = this.props.props.location.pathname.split('/')[2]
  if (contentID !== prevState.contentID) {
    this.props.getUser()
     .then(this.fetchContent(contentID))
     .then(this.fetchSidebarContent())
     // Update the state
     .then(this.setState({ contentID }))
  }
}

Then bind the url to the video element itself instead of using a source element (based on this post):

VideoPlayer:

<video width="720" height="480" src={this.props.src} controls controlsList="nodownload" autoPlay>
Baboo
  • 4,008
  • 3
  • 18
  • 33
  • Just the title of the video displayed on the page changes, the video itself keeps playing as the old one. It will only change to the correct one once I hit refresh. – Hassan Mar 17 '19 at 10:43