I’ve run into another hurdle when it comes to knowing the best way to structure something in React (specifically using a Flux architecture). Here’s my scenario:
I’ve got several components on a page that are audio players. If I hit the play button, I want to use Dispatcher.dispatch('TRACK_PLAY', {id: track._id}) to send to the track store that a track should begin playing. I see two ways of carrying this out:
Option 1: In getMeteorData
getMeteorData() {
// this AppState var is set by a dispatch call elsewhere
const trackId = AppState.get('artistTracks.playingTrackId');
if (trackId === this.props.track._id) {
this.play();
}
return {
playingTrackId: trackId
}
},
play() {
// this.refs.audio points to an <audio> tag
React.findDOMNode(this.refs.audio).play();
},
// ...
'TRACK_PLAY': function (action) {
action.domRef.play();
}
I’m guessing option #2 might be the way to go, as getMeteorData may possibly not be a great place to do anything more than supply data to the component.
The only caveat with option #2 is that the play/pause buttons are in one component, which contains another component that actually has the <audio> object. So I’d somehow have to trigger something within the actual player component to call the dispatch, because the parent that has the buttons wouldn’t be able to use this.refs.audio.
You have multiple audio components? ideally you want only only have one audio onject for the site and just switch the file inside it depend on which audio you play, correct me if i am wrong.
I am actually doing a project with audio playback as well. What i did to track play is to just call Meteor.call(‘incPlayCount’, trackId) when the click is called.
So the audio object/component will take isPlaying as a prop and pause and play it self depend on the prop change. Same thing with its source.
The only caveat with option #2 is that the play/pause buttons are in one component, which contains another component that actually has the object. So I’d somehow have to trigger something within the actual player component to call the dispatch, because the parent that has the buttons wouldn’t be able to use this.refs.audio.
That’s why you should only make one AudioPlayer that takes isPlaying as a prop, and updates it self depend on that prop change.
Now this player will manage playing itself when all you have to do is just change your app state. No need to make other components/stores direct do dom.Play() and Pause() on the element itself (which is whole point of react, components should really only takes care of rendering, especially when you are using Flux)
I don’t think passing the DOM element (option #2) is the way to go. Stores and Views (components) should not be aware of the existence of the other.
The communication between Stores and Views is done via State changes. So I would go with option #1 or like @sikanx suggested, with only one AudioTrack if that’s fine for your application.
By the way, regarding option #1, it looks like people is saying that using getMeteorData in the real React component is not a good practice. Instead, create a parent component which encapsulates your real React component and pass everything it needs via props. The parent component only renders the real component with the correct props, like for example a this.props.isPlaying === true. That way, your real component is React-only and reusable even outside the Meteor world.
I think you should decide if you want re-render’s based on changes in getMeteorData, or based on flux state.
There is not much control for you if you go getMeteorData way.
Well, I’d prefer render to be triggered due to Flux store state change. But how else would render be triggered, since getMeteorData is the only React component method that is fired when a reactive variable changes?