javascript - Umiestnite funkciu, ktorá vracia promise, do stavu redux


Neskôr vysvetlím, prečo to chcem urobiť. Tu je problém. Mám funkciu, ktorá vracia sľub takto:

const testFunc = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (Math.random() >  0.5) {
        resolve('succeeded');
      } else {
        reject('failed');
      }
    }, 1000);
  });
};

Ako sa očakávalo, môžem ho takto bez problémov vyvolať:

testFunc()
  .then((result) => console.log(result)) // 'succeeded'
  .catch((error) => console.log(error)); // 'failed'

Alebo to priradiť k premennej a zavolať to takto

const testFuncVar = testFunc;
testFuncVar()
  .then((result) => console.log(result)) // 'succeeded'
  .catch((error) => console.log(error)); // 'failed'

Tieto sú všetky očakávané. Avšak, keď funkciu vložím do stavu Redux a potom ju odtiaľ zavolám, už to nefunguje. Tu je to, čo som urobil (hrubo zjednodušené).

const initialState = {testFunc: testFunc};
// redux reducer, action creator, etc.
...
...
// somewhere later. Here I'm using redux-thunk to access redux state
function testFunInState() {
  return (dispatch, getState) => {
    const {testFunc} = getState();
    // this is not working
    testFunc()
      .then((result) => console.log(result))
      .catch((error) => console.log(error));
  };
}

Chybu, ktorú som dostal, bolo _promise2 is not defined. Všimnite si, že názov premennej _promise2 by mal pochádzať z babel transpileru. Ak urobím console.log(state.testFunc) alebo console.log(testFunc), dostanem:

testFunc() {
  return new _promise2.default(function (resolve, reject) {
    if (Math.random() > 0.5) {
      resolve('succeeded');
    } else {
      reject('failed');
    }
  });
}

Takže nejako sa objekt Promise stratil, kedykoľvek sa funkcia vloží do stavu redux?

Ale našiel som obchádzku. Ak zmením funkciu na

const testFunc = (resolve, reject) => {
  setTimeout(() => {
    if (Math.random() >  0.5) {
      resolve('succeeded');
    } else {
      reject('failed');
    }
  }, 1000);
};

A zavolajte ho s resolve a reject odovzdanými ako parametre, potom som v poriadku.

// this works
new Promise((resolve, reject) => state.testFunc(resolve, reject))
  .then((result) => console.log(result))
  .catch((error) => console.log(error));

Zaujímam sa, prečo funkcia, ktorá vracia promise, nefunguje, keď ju vložíte do redux store, ale tá, ktorá promise nevracia, funguje?

Teraz k dôvodu, prečo to chcem robiť týmto spôsobom. To, čo sa snažím dosiahnuť, je mať frontu úloh, ktorá periodicky spúšťa niektoré asynchrónne akcie, a v závislosti od výsledku (vybavené alebo zamietnuté) robí niečo iné (napríklad opakovanie alebo odoslanie oznámenia). Chcem dynamicky pridávať/odstraňovať úlohy do fronty a z fronty, preto je ťažké mať reducer, ktorý dokáže spracovať všetky možné akcie. Pripadá mi to ako rozumný prístup. Som otvorený návrhom.

Odpoveď č. 1

Po trochu podrobnejšom skúmaní som nakoniec zistil, že problém naozaj nemá nič spoločné s reduxom alebo promise. Je to jednoducho preto, že testFunc sa pridáva do stavu redux cez priradenie k objektu literálu initialState. Keď sa to robí týmto spôsobom, väzba sa stratí, a preto vzniká chyba _promise2 is undefined. V skutočnosti, ak pridám testFunc do stavu redux dynamicky (napr. cez action creatora a reducer), väzba sa zachová a všetko funguje úplne správne. Tu je podrobnejšie vysvetlenie problému https://stackoverflow.com/a/2702028/4401488.

Len pre referenciu, tu je kód na pridanie testFunc do stavu redux cez reducer

const initialState = {};
// redux reducer
export default function jobReducer(state = initialState, action = {}) {
  switch (action.type) {
    case ADD_JOB:
      return {job: action.job};
    default:
      return state;
  }
}

// action creator
export function addJob(job) {
  return {
    type: ADD_JOB,
    job: job
  };
}
...
// add testFunc to redux state via action creator,
// I'm using redux-thunk here to access dispatch
function addTestFun() {
  return (dispatch) => {
    dispatch(addJob(testFunc));
  };
}
...
// invocation of testFunc. Here I'm using redux-thunk to access redux state
function testFunInState() {
  return (dispatch, getState) => {
    const {testFunc} = getState();
    // Now this is working
    testFunc()
      .then((result) => console.log(result))
      .catch((error) => console.log(error));
  };
}

Odpoveď č. 2

Musíte používať middleware s Reduxom, ako je Redux Thunk

import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers/index';

// Note: this API requires redux@>=3.1.0
const store = createStore(
  rootReducer,
  applyMiddleware(thunk)
);

Potom napíš svoju akciu takto:

const INCREMENT_COUNTER = 'INCREMENT_COUNTER';

function increment() {
  return {
    type: INCREMENT_COUNTER
  };
}

function incrementAsync() {
  return dispatch => {
    setTimeout(() => {
      // Yay! Can invoke sync or async actions with `dispatch`
      dispatch(increment());
    }, 1000);
  };
}

Môžete tiež skúsiť použiť Redux-Promise