ฉันสามารถส่งการกระทำด้วยตัวลดได้หรือไม่?


197

เป็นไปได้ไหมที่จะส่งการกระทำในตัวลดเอง? ฉันมีแถบความคืบหน้าและองค์ประกอบเสียง เป้าหมายคือการอัพเดทแถบความคืบหน้าเมื่อเวลาได้รับการปรับปรุงในองค์ประกอบเสียง แต่ฉันไม่รู้ว่าจะวางตำแหน่งตัวจัดการเหตุการณ์ ontime หรือวิธีส่งการดำเนินการในการเรียกกลับของ 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

มันเป็นคลาสธรรมดา 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 แต่ฉันไม่รู้ว่าจะเรียกมันอย่างไรในเมธอด updateProgress หรือมีวิธีอื่นในการรับโปรแกรมเลือกจ่ายงาน อาจจะมีอุปกรณ์ประกอบฉาก? ขอบคุณ
klanm

ไม่มีปัญหา. คุณสามารถส่งผ่านในการดำเนินการกับMyAudioPlayerองค์ประกอบจากคอนเทนเนอร์หลักที่connected react-reduxด้วย ตรวจสอบวิธีการดำเนินการกับmapDispatchToPropsที่นี่: github.com/reactjs/react-redux/blob/master/docs/…
ebuat3989

6
สัญลักษณ์ที่updateProgressActionกำหนดไว้ในตัวอย่างของคุณอยู่ที่ไหน
Charles Prakash Dasari

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;และใช้งานได้ดี
zanerock

1
หากคุณไม่ควรส่งแอคชั่นภายในรีดิวเซอร์แล้ว Redux-thunk จะหักกฏของ redux หรือไม่?
Eric Wiener

มันทำงานอย่างไรเมื่อคุณพยายามจัดการกับการตอบกลับของ websocket? นี่เป็นค่าเริ่มต้นการส่งออกแบบลดของฉัน (สถานะการกระทำ) => {const m2 = [... state.messages, action.payload] ส่งคืน Object.assign ({}, สถานะ, {ข้อความ: m2,})} และ I STILL รับข้อผิดพลาดนี้ "ตรวจพบการกลายพันธุ์ของรัฐในระหว่างการ
แจกจ่าย

12

คุณอาจลองใช้ห้องสมุดเช่นRedux-เทพนิยาย จะช่วยให้วิธีที่สะอาดมากในการจัดลำดับฟังก์ชั่น async ดับการกระทำใช้ความล่าช้าและอื่น ๆ มันทรงพลังมาก!


1
คุณสามารถระบุวิธีการ 'จัดตารางเวลาการจัดส่งอื่นภายในตัวลด' ด้วย redux-saga ได้หรือไม่
XPD

1
@XPD คุณสามารถอธิบายอีกเล็กน้อยว่าคุณต้องการทำอะไรให้สำเร็จ หากคุณกำลังพยายามใช้แอคชั่นลดขนาดเพื่อส่งแอคชั่นอื่นคุณจะไม่สามารถทำอะไรคล้าย ๆ กับ redux-saga
chandlervdw

1
ตัวอย่างเช่นสมมติว่าคุณมีที่เก็บรายการที่คุณโหลดส่วนหนึ่งของรายการ รายการจะถูกโหลดอย่างเกียจคร้าน สมมติว่าสินค้ามีซัพพลายเออร์ ซัพพลายเออร์ยังโหลดอย่างเกียจคร้าน ดังนั้นในกรณีนี้อาจมีรายการที่ซัพพลายเออร์ไม่ได้โหลด ในตัวลดรายการถ้าเราต้องการรับข้อมูลเกี่ยวกับรายการที่ยังไม่ได้โหลดเราต้องโหลดข้อมูลจากเซิร์ฟเวอร์อีกครั้งผ่านตัวลด ในกรณีนั้น Redux-saga ช่วยในลดได้อย่างไร?
XPD

1
เอาล่ะสมมติว่าคุณต้องการยกเลิกคำขอนี้เพื่อขอsupplierข้อมูลเมื่อผู้ใช้พยายามเข้าชมitemหน้ารายละเอียด คุณจะยิงปิดฟังก์ชั่นที่ยื้อการดำเนินการพูดcomponentDidMount() FETCH_SUPPLIERภายในตัวลดขนาดคุณอาจติ๊กเครื่องหมายloading: trueเพื่อแสดงสปินเนอร์ในขณะที่ทำการร้องขอ redux-sagaจะรับฟังการกระทำนั้นและในการตอบสนองให้ปิดคำขอจริง FETCH_SUPPLIER_SUCCEEDEDฟังก์ชั่นการใช้เครื่องกำเนิดไฟฟ้าแล้วมันจะรอการตอบสนองและการถ่ายโอนข้อมูลลงใน ตัวลดจะปรับปรุงร้านด้วยข้อมูลผู้จำหน่าย
chandlervdw

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.