itsource

리듀서로 액션을 디스패치할 수 있습니까?

mycopycode 2023. 3. 18. 08:38
반응형

리듀서로 액션을 디스패치할 수 있습니까?

리듀서 자체에 액션을 디스패치할 수 있습니까?프로그레스바와 오디오 요소가 있습니다.목표는 오디오 요소에서 시간이 업데이트될 때 진행 표시줄을 업데이트하는 것입니다.그러나 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;

리듀서가 종료되기 전에 다른 디스패치를 기동하는 것은 안티패턴입니다리듀서 처음에 수신한 상태는 리듀서가 종료되었을 때 현재 어플리케이션 상태가 아니기 때문입니다., 리듀서 내에서 다른 디스패치를 스케줄 하는 것은 안티패턴이 아닙니다.사실 이것이 Elm 언어가 하는 일입니다.Redux는 아시다시피 Elm 아키텍처를 JavaScript에 도입하기 위한 시도입니다.

다음은 속성을 추가할 미들웨어입니다.asyncDispatch당신의 모든 행동에 대해서요.리듀서가 완료되어 새로운 애플리케이션 상태가 반환되면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 });;
  }
}

리듀서 내에서 액션을 디스패치하는 것은 안티패턴입니다리덕터는 부작용 없이 액션페이로드를 다이제스트하고 새 상태 개체를 반환하는 것입니다.리듀서 내에서 리스너를 추가하고 액션을 디스패치하면 액션 연쇄 및 기타 부작용이 발생할 수 있습니다.

초기화된 것 같습니다.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디스패치에 직접 전화할 필요는 없습니다.

리플렉스가 같은 도서관을 이용해보세요.이를 통해 비동기 함수의 시퀀스 처리, 실행 중지, 지연 사용 등을 매우 깔끔하게 수행할 수 있습니다.그것은 매우 강력합니다!

redux-loop은 Elm에서 힌트를 얻어 이 패턴을 제공합니다.

기술적으로 어떤 것이든 가능하기 때문에, 당신은 그것을 할 수 있습니다.하지만 당신은 그것을 하지 말아야 한다.

다음은 Dan Abramov(Redux의 제작자)의 말을 인용한 것입니다.

"리듀서 내부에 왜 디스패치를 원하십니까?그것은 도서관을 심하게 오용하고 있다.이는 React에서 setState inside 렌더링을 허용하지 않는 것과 정확히 동일합니다."

자신이 직접 만든 '감축기 안에서 금단 파견' 기투브 티켓에서

리듀서 내부에서 디스패치 및 액션이 발생하는 것 같다.

가 간단한 .useReducer인크루세

에서는, 「INCREASE 되어 「SUB 되어 「INCREASE」를 설정해 .cnt로 하고 , 「INCRESE해, 「INCRESE」를 설정합니다.cntINCRESE무시되었습니다)0인크레스하다, -1번이다.

참조: https://codesandbox.io/s/simple-react-context-example-forked-p7po7?file=/src/index.http:syslog-http:syslogs

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

언급URL : https://stackoverflow.com/questions/36730793/can-i-dispatch-an-action-in-reducer

반응형