red*_* 87 4 javascript audio reactjs redux
我目前正在构建一个音乐应用程序,并且在将音频对象及其当前状态存储在React / Redux中时,对使用音频对象的正确方法存在疑问。
我目前正在我的一个组件中调度一个动作,该动作将传递给以下reducer以将音频对象设置为状态的一部分:
Reducer.js
import { fromJS } from 'immutable';
const initialState = fromJS({
audioTrack: false
});
export const musicPlayer = (state = initialState, action) => {
switch (action.type) {
case 'musicPlayer/PLAY_TRACK': {
const mergeObj = {};
const audioTrack = state.get('audioTrack');
mergeObj.audioTrack = audioTrack;
if (!audioTrack) {
mergeObj.audioTrack = new Audio('../../public/music/test.mp3');
mergeObj.audioTrack.play();
} else if (state.get('audioTrack').paused) {
mergeObj.audioTrack.play();
} else {
mergeObj.audioTrack.pause();
}
return state.merge(mergeObj);
}
default: return state
}
}
Run Code Online (Sandbox Code Playgroud)
基本上,这里如果audioTrack是false我创建一个新的音轨当有人点击播放按钮。然后将audioTrack对象添加到reducers状态。从那里开始,如果设置了音轨,则可以audioTrack从reducers状态访问该对象,并在需要时暂停它,以及调用所需的任何其他音频方法。
我在这里的问题是,我敢肯定,将音频对象存储在reducer中不是解决这种问题的正确方法。音频对象具有一些深层嵌套的对象,出于明显的性能原因,我想使减速器尽可能平坦。
有什么更好的方法来解决这个问题?虽然我已经将音频对象添加到窗口对象,然后将其状态存储在那里,但是再次不确定这是否是最合理的方法。希望将所有音频元素都放在dom之外,以防止用户进行快速检查元素并找到该节点及其来源。
谢谢,如果有任何不清楚的地方,请告诉我!
首先,不建议使用更改状态键类型(audioObject从boolean到class Audio)的方法。您应该避免更改状态键的类型,因为它可能导致许多无法预料的错误。对于人类读者来说,在浏览代码时也很难弄清楚它的用途。
现在,看到您的用例(一次只跟踪一个音频),我觉得在您当前的代码中并没有真正明确关注点的分离,这就是为什么要在组件或还原器状态下保持audioTrack的困惑。让我们使用Redux进行改进。
我们将通过以下方式应用关注点分离。
actionCreator initialStatereducermusicPlayerreducerconnect以订阅Redux状态。mapStateToProps您的Redux状态将具有2个键:
audioTrack:跟踪活动音频轨道的文件名。isPlaying:跟踪曲目是播放还是暂停。您的actionCreator将如下所示:
// sets active track
export const setActiveTrack (activeTrack) => ({
type: 'musicPlayer/SET_ACTIVE_TRACK',
payload: activeTrack,
});
// plays active track
export const playTrack () => ({
type: 'musicPlayer/PLAY_TRACK',
payload: true,
});
// pauses active track
export const pauseTrack () => ({
type: 'musicPlayer/PLAY_TRACK',
payload: false,
});
Run Code Online (Sandbox Code Playgroud)
你的减速将工作如下:
//always use null as an indicator of empty.
const initialState = {
audioTrack: null,
isPlaying: false,
};
export const musicPlayer = (state = initialState, action) => {
switch (action.type) {
case 'musicPlayer/SET_ACTIVE_TRACK':
return {
...state,
audioTrack: action.payload,
}
case 'musicPlayer/PLAY_TRACK':
return {
...state,
isPlaying: action.payload,
}
default: return state
}
}
Run Code Online (Sandbox Code Playgroud)
您的mapStateToProps将如下所示:
mapStateToProps(state) {
return {
audioTrack: createSelector(state.audioTrack, audioTrack => new Audio(audioTrack)), // `createSelector` will return the same old Audio instance unless the `audioTrack` value changes
isPlaying: state.isPlaying
}
}
Run Code Online (Sandbox Code Playgroud)
最后是您的演示组件:
// Take care of side-effects in componentDidMount (for first render) or componentDidUpdate (for all other renders)
componentDidUpdate() {
if(this.props.audioTrack) {
const audioTrack = this.props.audioTrack;
if(this.props.isPlaying) audioTrack.play();
else audioTrack.pause();
}
}
render() {
return ...; // your presentation logic
}
Run Code Online (Sandbox Code Playgroud)
播放/暂停音频文件是一个副作用所应采取的护理componentDidUpdate,并componentDidMount根据反应16个准则。您可以在以下Notes部分中进行检查:https : //reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops
使用音频类包装音频文件是在mapStateToProps使用中完成的,reselect/createSelector因为createSelector如果的值state.audioTrack未更改,它将返回较旧的音频文件。这是播放音频文件后暂停所需的。
createSelector:https : //github.com/reduxjs/reselect#createselectorinputselectors--inputselectors-resultfunc
(我在这里假设用户一次只能处理一个文件。一旦他更改了音频文件,就可以将其丢弃。万一要维护一个活动音频文件列表,可以使用生成器函数,用于在给定文件名的情况下生成音频。)
性能
只要考虑到性能,您就不必担心大型Audio对象。除以外,它的参考没有存储在任何地方createSelector。当用户更改活动音频轨道时,较旧的轨道将在下一个GC周期中免费收集。
可扩展性
mapStateToProps。如果使用不当,将导致不必要的组件重新渲染。我建议您观看Redux Creator Dan Abramov制作的该视频系列,以了解如何使用Redux:https://egghead.io/courses/building-react-applications-with-idiomatic-redux
我已经展示了如何使用Redux集成您的用例,因为我知道您正在尝试学习Redux。但是对于这样一个简单的应用程序,您可能不需要Redux。您可以简单地使用容器(数据/状态)和表示组件的概念。看看Dan Abramov撰写的这篇精彩文章:https : //medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0
| 归档时间: |
|
| 查看次数: |
1118 次 |
| 最近记录: |