首页 文章

我可以在减速机中发送动作吗?

提问于
浏览
140

是否可以在减速机本身发送动作?我有一个进度条和一个音频元素 . 目标是在音频元素中更新时间时更新进度条 . 但是我不知道将ontimeupdate事件处理程序放在何处,或者如何在ontimeupdate的回调中调度一个动作来更新进度条 . 这是我的代码:

//reducer

const initialState = {
    audioElement: new AudioElement('test.mp3'),
    progress: 0.0
}

initialState.audioElement.audio.ontimeupdate = () => {
    console.log('progress', initialState.audioElement.currentTime/initialState.audioElement.duration);
    //how to dispatch 'SET_PROGRESS_VALUE' now?
};


const audio = (state=initialState, action) => {
    switch(action.type){
        case 'SET_PROGRESS_VALUE':
            return Object.assign({}, state, {progress: action.progress});
        default: return state;
    }

}

export default audio;

4 回答

  • 9

    Dispatching an action within a reducer is an anti-pattern . 您的reducer应该没有副作用,只需消化动作有效负载并返回一个新的状态对象 . 在reducer中添加侦听器和调度操作可能会导致链接操作和其他副作用 .

    听起来像是初始化的 AudioElement 类,事件监听器属于组件而不是状态 . 在事件监听器中,您可以调度一个操作,该操作将更新状态中的 progress .

    您可以在新的React组件中初始化 AudioElement 类对象,也可以只将该类转换为React组件 .

    class MyAudioPlayer extends React.Component {
      constructor(props) {
        super(props);
    
        this.player = new AudioElement('test.mp3');
    
        this.player.audio.ontimeupdate = this.updateProgress;
      }
    
      updateProgress () {
        // Dispatch action to reducer with updated progress.
        // You might want to actually send the current time and do the
        // calculation from within the reducer.
        this.props.updateProgress();
      }
    
      render () {
        // Render the audio player controls, progress bar, whatever else
        return <p>Progress: {this.props.progress}</p>;
      }
    }
    
    class MyContainer extends React.Component {
       render() {
         return <MyAudioPlayer updateProgress={this.props.updateProgress} />
       }
    }
    
    function mapStateToProps (state) { return {}; }
    
    return connect(mapStateToProps, {
      updateProgressAction
    })(MyContainer);
    

    请注意 updateProgressAction 会自动用 dispatch 包装,因此您无需直接调用dispatch .

  • 113

    Starting another dispatch before your reducer is finished is an anti-pattern ,因为当您的减速器完成时,您在减速器开始时收到的状态将不再是当前的应用状态 . 但 scheduling another dispatch from within a reducer is NOT an anti-pattern . 事实上,这就是Elm语言所做的,正如您所知道的,Redux是尝试将Elm架构引入JavaScript .

    这是一个中间件,它将属性 asyncDispatch 添加到您的所有操作中 . 当您的reducer完成并返回新的应用程序状态时, asyncDispatch 将触发 store.dispatch ,无论您给它做什么操作 .

    // This middleware will just add the property "async dispatch"
    // to actions with the "async" propperty set to true
    const asyncDispatchMiddleware = store => next => action => {
      let syncActivityFinished = false;
      let actionQueue = [];
    
      function flushQueue() {
        actionQueue.forEach(a => store.dispatch(a)); // flush queue
        actionQueue = [];
      }
    
      function asyncDispatch(asyncAction) {
        actionQueue = actionQueue.concat([asyncAction]);
    
        if (syncActivityFinished) {
          flushQueue();
        }
      }
    
      const actionWithAsyncDispatch =
        Object.assign({}, action, { asyncDispatch });
    
      const res = next(actionWithAsyncDispatch);
    
      syncActivityFinished = true;
      flushQueue();
    
      return res;
    };
    

    现在你的减速机可以做到这一点:

    function reducer(state, action) {
      switch (action.type) {
        case "fetch-start":
          fetch('wwww.example.com')
            .then(r => r.json())
            .then(r => action.asyncDispatch({ type: "fetch-response", value: r }))
          return state;
    
        case "fetch-response":
          return Object.assign({}, state, { whatever: action.value });;
      }
    }
    
  • 88

    您可以尝试使用像redux-saga这样的库 . 它允许以非常简洁的方式对异步函数进行排序,触发操作,使用延迟等 . 它非常强大!

  • 3

    redux-loop从Elm获得提示并提供此模式 .

相关问题