감속기에서 작업을 전달할 수 있습니까?


197

감속기 자체에서 작업을 전달할 수 있습니까? 진행률 표시 줄과 오디오 요소가 있습니다. 오디오 요소에서 시간이 업데이트 될 때 진행률 표시 줄을 업데이트하는 것이 목표입니다. 그러나 진행률 표시 줄을 업데이트하기 위해 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? 그것은 상태가 아닌 것 같습니다.
ebuat3989

Audio Object를 보유한 ES6 일반 클래스 (무 반응)입니다. 상태가 아닌 경우 재생 / 정지, 건너 뛰기 등을 어떻게 제어합니까?
klanm

2
redux saga
Kyeotic

답변:


151

감속기 내에서 작업을 전달하는 것은 안티 패턴 입니다. 리듀서는 부작용이 없어야합니다. 액션 페이로드를 소화하고 새로운 상태 객체를 반환하면됩니다. 리듀서 내에 리스너를 추가하고 조치를 디스패치하면 연쇄 조치 및 기타 부작용이 발생할 수 있습니다.

초기화 된 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직접 전화 파견 할 필요가 없습니다.


설명해 주셔서 감사합니다! 그러나 나는 여전히 디스패처에 액세스하는 방법을 모른다. 나는 항상 react-redux의 connect 메소드를 사용했습니다. 그러나 updateProgress 메소드에서 호출하는 방법을 모르겠습니다. 또는 발송자를 얻는 다른 방법이 있습니까? 소품과 함께? 감사합니다
klanm

문제 없어요. 와 함께 제공 MyAudioPlayer되는 상위 컨테이너에서 구성 요소로 작업을 전달할 수 있습니다 . 로 그 작업을 수행하는 방법을 확인 여기 : github.com/reactjs/react-redux/blob/master/docs/...connectreact-reduxmapDispatchToProps
ebuat3989

6
updateProgressAction예제에서 기호는 어디에 정의되어 있습니까?
찰스 프라 카쉬 다사리

2
리듀서 내에서 액션을 전달하지 않으면 redux-thunk가 redux 규칙을 어 기고 있습니까?
Eric Wiener

2
@EricWiener 필자는 redux-thunk감속기가 아닌 다른 작업에서 작업을 전달 한다고 생각 합니다. stackoverflow.com/questions/35411423/…
sallf

162

리듀서가 완료되기 전에 수신 한 상태가 더 이상 리듀서가 완료 될 때 현재 애플리케이션 상태가 아니기 때문에 리듀서가 완료되기 전에 다른 디스패치를 ​​시작하는 것은 안티 패턴 입니다. 그러나 감속기 내에서 다른 디스패치를 ​​예약하는 것은 반 패턴이 아닙니다 . 사실 이것은 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 });;
  }
}

7
Marcelo, 귀하의 블로그 게시물은 귀하의 접근 상황을 설명하는 훌륭한 일을하므로 여기에 연결하고 있습니다 : lazamar.github.io/dispatching-from-inside-of-reducers
Dejay Clayton

3
미들웨어를 그대로 dispatch두고 조치를 리턴해야한다는 점을 제외하고는 이것이 내가 필요한 것 입니다. 나는 마지막 줄을 다음 const res = next(actionWithAsyncDispatch); syncActivityFinished = true; flushQueue(); return res;과 같이 바꿨다 .
자네 락

1
리듀서 내에서 액션을 전달하지 않으면 redux-thunk가 redux 규칙을 어 기고 있습니까?
에릭 위너

웹 소켓 응답을 처리하려고 할 때 어떻게 작동합니까? 이것은 내 감속기 내보내기 기본값입니다 (상태, 동작) => {const m2 = [... state.messages, action.payload] return Object.assign ({}, state, {messages : m2,})} 그리고 여전히 이 오류가 발생합니다 "상태 변이가 디스패치 사이에서 발견되었습니다"
quantumpotato

12

redux-saga 와 같은 라이브러리를 사용해보십시오 . 비동기 함수를 시퀀싱하고, 액션을 끄고, 지연을 사용 하는 등 매우 깨끗한 방법을 제공합니다. 매우 강력합니다!


1
redux-saga로 '레 듀서 내부에 다른 디스패치 예약'을 달성하는 방법을 지정할 수 있습니까?
XPD

1
@XPD 당신이 이루고 싶은 것을 조금 더 설명해 주시겠습니까? 다른 액션을 전달하기 위해 리듀서 액션을 사용하려는 경우 redux-saga와 유사한 기능을 사용할 수 없습니다.
chandlervdw

1
예를 들어, 품목의 일부를로드 한 품목 저장소가 있다고 가정하십시오. 품목이 느리게 적재됩니다. 품목에 공급 업체가 있다고 가정하십시오. 공급 업체도 느리게 적재했습니다. 따라서이 경우 공급 업체가로드하지 않은 품목이있을 수 있습니다. 아이템 리듀서에서 아직로드되지 않은 아이템에 대한 정보를 얻으려면 리듀서를 통해 서버에서 데이터를 다시로드해야합니다. 그렇다면 redux-saga는 리듀서 내부에서 어떻게 도움이됩니까?
XPD

1
supplier사용자가 item세부 정보 페이지 를 방문하려고 할 때 정보에 대한이 요청을 시작한다고 가정 해 보겠습니다 . 귀하의 componentDidMount()작업을 전달하는 기능을 해고 말한다 FETCH_SUPPLIER. 리듀서 내에서 loading: true요청하는 동안 스피너를 표시하기 위해 a 와 같은 것을 체크 할 수 있습니다 . redux-saga해당 조치를 수신하고 응답하여 실제 요청을 해제합니다. 생성기 기능을 사용하여 응답을 기다렸다가로 덤프 할 수 FETCH_SUPPLIER_SUCCEEDED있습니다. 그런 다음 감속기는 공급 업체 정보로 상점을 업데이트합니다.
chandlervdw

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.