Let's say I am building an audio player which consists of a control panel, where user can pause/play currently selected track, and audio players themselves. Possible actions are to pause/play the track, and the audio player component is listening to state changes and deciding whether to play/pause the track (example code taken from https://stackoverflow.com/a/50880480/18547676)
Actions:
// plays active track
export const playTrack () => ({
type: 'musicPlayer/PLAY_TRACK',
payload: true,
});
// pauses active track
export const pauseTrack () => ({
type: 'musicPlayer/PLAY_TRACK',
payload: false,
});
Reducer:
const initialState = {
isPlaying: false,
};
export const musicPlayer = (state = initialState, action) => {
switch (action.type) {
case 'musicPlayer/PLAY_TRACK':
return {
...state,
isPlaying: action.payload,
}
default: return state
}
}
Component (state mapped to props):
componentDidUpdate() {
if(this.props.audioTrack) {
// What to do in case audioTrack.play() errors out?
if(this.props.isPlaying) audioTrack.play();
else audioTrack.pause();
}
}
My question is the following; what is the recommended way to handle the cases when for example, audio player could not execute the action due to error. For example, user wanted to pause the current player; state changed, and audio player received the update, but was unable to perform the action due to error (in which case Redux state and the actual player state are not in sync). Is there some pattern to handle this?
It depends on the actual audio player you're using. The browser's native one for example has the option to attach event listeners: https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/onerror
You could use this to sync error state with redux:
audioElem.onerror = () => {
dispatch(setError(audioElem.error);
};
The general idea would be that if the audio interface emits events, you dispatch redux actions. And user interaction, like pressing a play button, dispatches actions that call methods on the audio interface as a side effect (example using thunk middleware):
const playAction = () => async (dispatch, getState) => {
dispatch(playRequested());
try {
await audioElem.play();
dispatch(playStarted());
} catch (error) {
dispatch(playFailed(error));
}
};