Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Put function that returns promise in redux state

I'll explain why I want to do this later. Here is the problem. I have a function that returns a promise like below:

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

As expected, I can invoke it like this just fine:

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

Or assign it to a variable and call it like this

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

These are all expected. However, once I put the function in redux state, then invoke it from there, it doesn't work anymore. Here is what I did (grossly simplified).

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));
  };
}

The error I got was _promise2 is not defined. Note that the variable name _promise2 should be from babel transpiler. If I console.log(state.testFunc) or console.log(testFunc), I got:

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

So somehow the Promise object got lost whenever the function is put in redux state?

But I did find a workaround. If I change the function to

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

And call it with resolve and reject passed in as parameters, then I'm good.

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

I'm wondering why a function that returns promise doesn't work once it's put in redux store, but one without returning promise works?

Now onto why I want to do it this way. What I'm trying to achieve is to have a job queue that periodically dispatch some async actions, and depending on the result (resolve or reject), does something else (like retry, or send out notification). I want to dynamically add/remove jobs to and from the queue, therefore it's hard to have a reducer that can handle all possible actions. It feels like this is a reasonable way to approach it. I'm open to suggestions.

like image 844
realbug Avatar asked Jul 01 '16 21:07

realbug


People also ask

How do I use promise in Redux?

Using Redux-Promise in React AppYou can use a promise to represent a value based on the request data, either the resolved value or why the request is unresolved. JavaScript uses promises subscribed by a function, and the same function knows how to request status and resolve or reject the promise.

Does Dispatch return promise?

dispatch returns something that is not a promise.

Is Redux async action?

As it turns out, Redux already has an official version of that "async function middleware", called the Redux "Thunk" middleware. The thunk middleware allows us to write functions that get dispatch and getState as arguments.

How do I link a functional component to Redux?

The connect() function connects a React component to a Redux store. It provides its connected component with the pieces of the data it needs from the store, and the functions it can use to dispatch actions to the store.


1 Answers

With a bit more digging, I finally figured that the problem really has nothing to do with redux or promise. It's simply because they way testFunc getting added to redux state through assignment to initialState object literal. When it's done this way, the binding is lost, hence the _promise2 is undefined error. Actually if I add testFunc to redux state dynamically (e.g. via action creator and reducer), the binding got preserved and everything works just fine. Here is a more detail explanation of the issue https://stackoverflow.com/a/2702028/4401488.

Just for reference, here is the code for adding testFunc to redux state via 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));
  };
}
like image 157
realbug Avatar answered Oct 30 '22 23:10

realbug