Keep reference to new operator - javascript

I have multiple video's on a page. When I click the play button on one of the video's (app.js) the data is retrieved and myVideo.init is called (video.js). This works but the problem is that the data of the previous video instance is always overwritten, I would somehow like to keep this intact because it contains information on when the player was started/paused etc. In theory it would need to be possible to:
Start video A
Pause video A
Start video B
Start video A again from the same coordinates it was paused, but this data was lost when I started video B.
I hope my question is clear enough. Help would be much appreciated.
// video.js
export default new (Base.extend({
myVideo: null,
init(clip) {
this.myVideo = new videoObject({}, 'https://localhost.nl?video123');
this.myVideo.setProperties()
}
}))();
// app.js
export default Base.extend({
state: {
isFirstTimePlaying: []
},
util.getJson({url: 'test.json'}).done((data) => {
let id = this.getPlayerId();
if (!this.state.isFirstTimePlaying.includes(id)) {
myVideo.init(data, id);
this.state.isFirstTimePlaying.push(id);
}
});
}))();

Related

Arranging JSX according to a certain algorithm

I'm making a simple web app that helps people organize teams by skill level, you enter the player name, skill level ( 0 - 10 ), and the player's avatar.
For example, you add 3 players in team1 and 4 in team2 and then you get them displayed in a way where the sum of skill value of team1 is equal to team2.
I ended up making the app create a new player instance each time the user presses the add button and then push that instance to an array. just like you see here:
class Player {
constructor(name, image, skill) {
this.name = name;
this.image = image;
this.skill = skill;
}
}
const createPlayer = (name, image, skill) => {
// creates a player with given data and pushes it to the playersList array
const playerIns = new Player(name, image, skill);
playersList.push(playerIns);
};
Then the app maps through the playersList array and returns a list element for each player just like a basic todo-app.
I need help with implementing the logic that organizes the players correctly as described above.
I can't really think of a way to make this work so I'd appreciate any help.

Single instance of howler player in React/NextJS

I have many audios on every page my app render (in NextJS). I'd like that when an user clicks on an audio button any other audio playing stops (I want to have just one audio playing at the time). How can I achieve this? (I'm using Howler)
My idea was to have a single instance of Howler as singleton it? is it possible to have a single instance of a function and share it across the app? with React Context? (but they're not unique, right?)
Why don't you create a React context and store the Howler instance in there?
https://reactjs.org/docs/context.html
export default ({ children }) => {
const [howler, setHowler] = useState(new Howl({ ... }));
return (
<HowlerContext.Provider value={howler}>
{children}
</AmplitudeContext.Provider>
);
};
This way you can share it with every child to the provider

Next steps in a react/redux sound dispatch app

I'm building a react/redux drum machine app for a freeCodeCamp challenge, and I've got the buttons done and the connections made for onClick to dispatch an action with the url link to the sound as a property, but I'm hung up on what I need to create as a reducer for this to work how I'm wanting.
You can view what I've got so far here: https://codesandbox.io/s/k29r3928z7 , and you can ignore the pieces for the Hello/Goodbye switch at the top as that was just for me to understand the flow of React/Redux to start. My main question is what should I be doing with state in this case? Pressing the button won't really change the state at all, so I'm not sure what I should be doing at the 'new state' step of creating a reducer.
I am not familiar with the specific goals of the freeCodeCamp challenge, however if you're wanting the app to play the correct sound when the user clicks any one of the buttons shown, you can achieve this by updating your drumClickHit action as follows:
export const drumClickHit = url => {
//Create an audio object from the url provided to the action
const sound = new Audio(url);
//Play the audio object immediatly
sound.play();
return {
type: "CLICKHIT",
url
};
};
To illustrate the relationship between action creators and reducers, you could achieve the same result as above, by adding the following "CLICKHIT" case to your reducer as follows:
case "CLICKHIT": {
//Create an audio object from the url provided to the action.
//Get the url from the action
const sound = new Audio(action.url);
//Play the audio object immediatly
sound.play();
return state;
}
In the case of your app however, logic for audio playback is better placed in the action, rather than reducer.

Redux architecture for a non-ui application

I am working on an html5 audio player and I am trying to figure out if Redux would be a good solution even without a UI for the state (data) to flow in.
I will have multiple modules:
Playback Module: Encapsulate all callbacks from the Audio Element and dispatch an action after each callback to update the state.
Streaming Module: All the logic to figure out which segment is need at a given time, which segment to prefetch, ...
MediaSource Module: The media source module is wrapper of all actions of the media source and the source buffer.
Question 1:
the Audio Element has it's own state, playing, paused, seeking, currentTime, ... and the Redux state reflects the state of the Audio Element.
Is that a good practice? I feel a bit concerned about having 2 states in different places and out of sync state...
Question 2:
Who is updated first?
Let's imagine I want to Pause my player:
//Implementation 1:
function pause() {
dispatch({type:'PLAYBACK_PAUSED'}).then(()=> {
this.audio.paused = true;
});
}
// Implementation 2:
function pause() {
dispatch({type:'PLAYBACK_PAUSED'});
this.audio.paused = true;
}
// Implementation 3:
function pause() {
dispatch({type:'PLAYBACK_PAUSED'});
onPause();
}
function onPause() {
const state = getState();
if (this.audio.paused != state.paused) {
this.audio.paused = state.paused;
}
}
Question 3:
Every 0.5 second, the Audio element triggers a callback with a new current time. When this callback is trigger, I dispatch an action to update the current time in the state.
Now, the Streaming Module needs to be aware of that change in order to figure out which segment is needed at this given time and needed to know if we need to prefetch future segments.
How is my Streaming Module supposed to be aware of that state change?
Like that?:
currentTime = 0;
...
const state = getState();
if (state.currentTime !== currentTime){
// Do Something...
// Do Something Else...
}
Thank you
Let me try to tackle your questions one by one:
Question 1
Assuming that you are using the HTML5 audio component: there is nothing wrong about a redux state augmenting/syncing with a state of another component such as your audio player. In all cases, though, I suggest that the redux state follows the audio components state (should there be a delay for example)
If, however, you built the audio component yourself, maybe you can wire it into the redux ecosystem by dispatching events from it directly.
Question 2
Like I said in the previous answer, I would always go with pausing the actual component first, THEN update the state once the component acknowledges the pause. Code wise, this would mean triggering a dispatch when the audio component has fully paused (on an onPause callback for example). The reason for following the audio element in terms of state rather than expecting it to follow our state is because the element might not pause synchronously/immediately which could bring our states out of sync.
Question 3
When you dispatch the event to update the redux state every 0.5s, you could dispatch another event to your streaming module. For this, I suggest using an asynchronous action creator like so:
import StreamingModule from 'your-streaming-module';
export function updateTime (timeInMs) {
return (dispatch) => {
dispatch({
type: YOUR_UPDATE_ACTION
time: timeInMs
});
// pseudo
StreamingModule.fetchSegment(timeInMs);
};
}
An alternative would be to subscribe to the store state directly. This all depends on how your streaming module is structured/instantiated:
import { createStore } from 'redux';
import StreamingModule from 'your-streaming-module';
const store = createStore(...);
store.subscribe(() => {
StreamingModule.fetchSegment(store.getState().time.ms);
});
The latter might not work well unless you save the current time somewhere before you fetch the segment.
Hope this helps.

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?
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.
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;
}
}

Categories

Resources