Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Redux to Manage a Video Player

What would be the ideal way to manage a video player using Redux, primarily in terms of dispatching actions to play/pause a video?

I'm working on building a video player in a React application, and I have event listeners on the video player dispatching relevant events to update the state. What would be the best way to have the parent component act on other components dispatching a PLAY or PAUSE action?

One use case that I would want to account for, for example, is one video being played and all other videos making sure to pause their playback.

Two ways that I've thought of would be:

1) Have the parent component check for changes in componentWillReceiveProps, and checking for something like

if (this.props.paused && !nextProps.paused) {
    this.refs.video.play()
}

2) Store a reference to the underlying video element in the state tree, and using middleware to act on certain actions (such as a PLAY action), such as

if (action.type === PLAY) {
    let state = getState();
    state.players[action.payload.playerId].play();
}

Would one strategy be more "correct" than the other, or is there another way that would make more sense?

like image 841
Andrew Burgess Avatar asked Mar 09 '16 18:03

Andrew Burgess


2 Answers

1) is the way to go.

Your video is simply a view component. In React you always want the component's props to dictate the output.

The problem with 2) is that the video object doesn't belong in the state. You're telling Redux too much about the implementation detail. Redux doesn't care about the implementation detail; it's just a state container.

UPDATE

On further reflection, I recommend componentDidUpdate() is the best place to place this logic. i.e.

componentDidUpdate(prevProps)
  if (prevProps.paused && !this.props.paused) {
    this.refs.video.play();
  }
}

The advantage being that componentDidUpdate() is called after re-rendering. It may not make a difference for your purposes, but it's possible that triggering a DOM event before React has had a chance to update the DOM may cause some unfortunate race conditions.

Of course this doesn't change the gist of my advice that output should always be props (or state) driven.

like image 102
David L. Walsh Avatar answered Oct 04 '22 08:10

David L. Walsh


I don't think that you need (2), (1) is pretty good. Now you handle only play action, but you can add pause there, like this:

if (this.props.paused && !nextProps.paused) {
  this.refs.video.play()
}

if (!this.props.paused && nextProps.paused) {
  this.refs.video.pause()
}

In this case your html player state will be synchronized with redux player state.

One use case that I would want to account for, for example, is one video being played and all other videos making sure to pause their playback.

In this case you can handle this situation in your reducer. I'd store all players by ids with paused state, like:

{
 1: {
   paused: true, 
 },
 2: {
   paused: false,
 },
 ...
}

You need to add player id in your action and pause other players, when you receive PLAY action:

function players(state = {}, action) {
  switch (action.type) {
    case PAUSE: 
      return {
        ...state,
        [action.id]: {
          paused: true
        }
      }
    case PLAY: 
      const nowPlaying = Object.keys(state).find(id => !state[id].paused);
      const newState = {
        ...state,
        [action.id]: {
          paused: false
        }
      };
      if (nowPlaying) {
        newState[nowPlaying].paused = true;
      }
      return newState;
  }
}
like image 30
Alexandr Subbotin Avatar answered Oct 04 '22 08:10

Alexandr Subbotin