[reactjs] 감속기에서 작업을 전달할 수 있습니까?
감속기 자체에서 작업을 전달할 수 있습니까? 진행률 표시 줄과 오디오 요소가 있습니다. 오디오 요소에서 시간이 업데이트 될 때 진행률 표시 줄을 업데이트하는 것이 목표입니다. 그러나 진행률 표시 줄을 업데이트하기 위해 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;
답변
감속기 내에서 작업을 전달하는 것은 안티 패턴 입니다. 리듀서는 부작용이 없어야합니다. 액션 페이로드를 소화하고 새로운 상태 객체를 반환하면됩니다. 리듀서 내에 리스너를 추가하고 조치를 디스패치하면 연쇄 조치 및 기타 부작용이 발생할 수 있습니다.
초기화 된 AudioElement
클래스 및 이벤트 리스너와 같은 소리 는 상태가 아닌 구성 요소 내에 속합니다. 이벤트 리스너 내에서 액션을 디스패치하면 progress
상태 가 업데이트 됩니다.
AudioElement
새로운 React 컴포넌트에서 클래스 객체를 초기화 하거나 해당 클래스를 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
직접 전화 파견 할 필요가 없습니다.
답변
리듀서가 완료되기 전에 수신 한 상태가 더 이상 리듀서가 완료 될 때 현재 애플리케이션 상태가 아니기 때문에 리듀서가 완료되기 전에 다른 디스패치를 시작하는 것은 안티 패턴 입니다. 그러나 감속기 내에서 다른 디스패치를 예약하는 것은 반 패턴이 아닙니다 . 사실 이것은 Elm 언어가하는 일이며, Redux는 Elm 아키텍처를 JavaScript로 가져 오려는 시도입니다.
다음은 asyncDispatch
모든 작업에 속성 을 추가하는 미들웨어입니다 . 리듀서가 완료되어 새 애플리케이션 상태를 반환하면 사용자가 지정한 조치로 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 });;
}
}