我可以在减速机中调度动作吗?

是否有可能在减速器本身调度一个动作?我有一个进度条和一个音频元素。目标是在音频元素中的时间更新时更新进度条。但是我不知道在哪里放置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;
176115 次浏览

在减速器中调度操作是一种反模式。您的减速器应该没有副作用,只需消化操作有效负载并返回一个新的状态对象。在减速器中添加侦听器和分派操作可能导致链式操作和其他副作用。

听起来像你初始化的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.updateProgressAction();
}


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。

你可以尝试使用像redux-saga这样的库。它允许用非常干净的方式来排序异步函数、触发动作、使用延迟等。这是非常强大的!

在减速机完成之前启动另一个分派是一种反模式,因为当减速机完成时,您在减速机开始时接收到的状态将不再是当前应用状态。但从减速器内部调度另一个分派不是反模式。事实上,这就是Elm语言所做的,正如您所知,Redux是将Elm体系结构引入JavaScript的一种尝试。

下面是一个中间件,它会将属性asyncDispatch添加到你的所有动作中。当你的reducer完成并返回新的应用程序状态时,asyncDispatch将用你给它的任何动作触发store.dispatch

// This middleware will just add the property "async dispatch" to all actions
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 });;
}
}

redux-loop从Elm得到一个提示,并提供了这个模式。

减速机内部的调度和动作似乎出现bug。

我用useReducer做了一个简单的反例,其中"被派遣,然后“;SUB"也。

在这个例子中,我期望"INCREASE"被派遣时也"SUB"和,设置cnt为-1,然后 继续“INCREASE"操作将cnt设置为0,但它是-1 ("INCREASE"被忽视)< / p > < p >看到这个: https://codesandbox.io/s/simple-react-context-example-forked-p7po7?file=/src/index.js:144-154 < / p >

let listener = () => {
console.log("test");
};
const middleware = (action) => {
console.log(action);
if (action.type === "INCREASE") {
listener();
}
};


const counterReducer = (state, action) => {
middleware(action);
switch (action.type) {
case "INCREASE":
return {
...state,
cnt: state.cnt + action.payload
};
case "SUB":
return {
...state,
cnt: state.cnt - action.payload
};
default:
return state;
}
};


const Test = () => {
const { cnt, increase, substract } = useContext(CounterContext);


useEffect(() => {
listener = substract;
});


return (
<button
onClick={() => {
increase();
}}
>
{cnt}
</button>
);
};


{type: "INCREASE", payload: 1}
{type: "SUB", payload: 1}
// expected: cnt: 0
// cnt = -1

因为任何事情在技术上都是可能的,你可以去做。但是你做它。

以下是Dan Abramov (Redux的创造者)的一句话:

"为什么你要调度里面的减速机?这是严重 误用库。这和React不允许你做的完全一样

From "禁止从减速机内部调度"他自己创建的Github门票