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?
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.
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.
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;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With