Чи можна відправити дію в редукторі?


197

чи можна відправити дію в самому редукторі? У мене є панель прогресу та звуковий елемент. Мета - оновити панель прогресу, коли час оновлюється в аудіоелементі. Але я не знаю, де розмістити ontimeupdate eventhandler або як відправити дію в зворотному дзвінку 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 (без реакції), що містить аудіооб'єкт. Якби його не було в штаті, як би я контролював гру / зупинку, пропуск тощо?
клан

2
Ви можете заглянути в
редукційну

Відповіді:


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тому вам не потрібно безпосередньо телефонувати з відправленням.


дуже дякую за роз’яснення! Але я досі не знаю, як отримати доступ до диспетчера. Я завжди використовував метод підключення від реакції-редукції. але я не знаю, як це викликати у методі updateProgress. Або є інший спосіб отримати диспетчера. може, з реквізитом? дякую
klanm

Нема проблем. Ви можете передати дію до MyAudioPlayerкомпонента з батьківського контейнера, який connectредагується react-redux. Дізнайтеся, як це зробити mapDispatchToPropsтут: github.com/reactjs/react-redux/blob/master/docs/…
ebuat3989

6
Де updateProgressActionу вашому прикладі визначено символ ?
Чарльз Пракаш Дасарі

2
Якщо ви не повинні відправляти дію в рамках редуктора, то чи не редукція-порушення є порушенням правил скорочення?
Ерік Вінер

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
Марсело, ваша публікація в блозі чудово описує обставини вашого підходу, тому я посилаюся
Dejay Clayton

3
Це було саме те, що мені було потрібно, за винятком перерв середнього програмного забезпечення, dispatchякі повинні повернути дію. Я змінив останні рядки на: const res = next(actionWithAsyncDispatch); syncActivityFinished = true; flushQueue(); return res;і це спрацювало чудово.
zanerock

1
Якщо ви не повинні відправляти дію в рамках редуктора, то чи не редукція-порушення є порушенням правил скорочення?
Ерік Вінер

Як це працює, коли ви намагаєтеся обробити відповіді веб-розетки? Це мій стандартний експорт для скорочення (стан, дія) => {const m2 = [... state.messages, action.payload] return Object.assign ({}, стан, {повідомлення: m2,})} отримати цю помилку, "мутація стану була виявлена ​​між розсилками"
kvantpotato

12

Ви можете спробувати використовувати бібліотеку на зразок redux-saga . Це дозволяє дуже чистим способом послідовності функцій асинхронізації, відключення дій, використання затримок тощо. Це дуже потужно!


1
Ви можете вказати, як домогтися "планування чергової відправки всередині редуктора" за допомогою redux-saga?
XPD

1
@XPD Ви можете пояснити трохи більше того, що ви хочете досягти? Якщо ви намагаєтесь використати скорочувальну дію для відправки іншої дії, ви не зможете без чогось схожого на редук-сагу.
chandlervdw

1
Наприклад, припустимо, у вас є магазин товарів, куди ви завантажили частину елементів. Елементи завантажуються ліниво. Припустимо, товар має постачальника. Постачальники також ліниво завантажуються. Так що в цьому випадку може бути товар, його постачальник не завантажений. У редукторі елементів, якщо нам потрібно отримати інформацію про ще не завантажений елемент, нам доведеться знову завантажувати дані з сервера через редуктор. У такому разі як редук-сага допомагає всередині редуктора?
XPD

1
Гаразд, скажімо, ви хотіли закрити цей запит на supplierінформацію, коли користувач намагається відвідати сторінку itemдеталей. Ви componentDidMount()б випалити функцію , яка відправляє дію, скажімо FETCH_SUPPLIER. У межах редуктора ви можете loading: trueпоставити галочку на зразок a, щоб показати спінер під час виконання запиту. redux-sagaбуде слухати цю дію, а у відповідь відмовитись від фактичного запиту. Використовуючи функції генератора, він може потім чекати відповіді і скидати її FETCH_SUPPLIER_SUCCEEDED. Потім редуктор оновлює магазин з інформацією про постачальника.
chandlervdw

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.