4
votes

I'm having this issue when I'm trying to invoke a callback after the promise resolves (using .then) It turns out that this gives my const request some kind of different promise that reducer returns as undefined:

action:

   export function lookup(company, callback) {
      const id = company.textfield;
      const url = `${ROOT_URL}${id}`;

      const request = axios.get(url)
       .then(() => callback())

      return {
        type: LOOK_UP,
        payload: request,
        meta: id
     };
 }

reducer:

import { LOOK_UP } from '../actions/index';

export default function(state = {}, action) {
    switch (action.type) {
        case LOOK_UP:
            const data = action.payload.data;
            const id = action.meta;

            if (data.Success === true) {
                return { ...state, [id]: data.CompanyInformation };
            } else {
                return state;
            }
    }
    return state;
}

As you can see I pass the data from API that axios get to my reducer. After this I set the state and I need to invoke that callback that is in 'actions' (it creates then another action call inside component). Unfortunatelly, I got error that in reducer const data = action.payload.data is undefined.

When I don't use this callback everything works fine, but the case is that I neet to invoke that callback only after this reducer returns new state.

2
Any progress? Have you had time to try my suggestions? - jonahe
I see now, this time the callback is invoked, but I think that the problem here is that it's fired too soon. I mean, this callback relays on the new state returned from reducer with this LOOK_UP action. I have to make another function that will fire up after this state is changed and not only when the promise is resolved. - Mateusz Szymajda
Does the callback really rely on the new state, or just the return from request = axios.get(url)? If it's only that the callback need the response as an argument, then you can simply use my first suggestion but with callback(dataFromRequest); Or callback(dataFromRequest.data); or what ever info you are interested in. You can also include the if-statement. etc. and do Promise.reject( ) if dataFromRequest.data.SUCESS !== true . - jonahe
What library are you using for the promises? You have the tag redux-promise. Is is this one? github.com/acdlite/redux-promise The examples are so few there that it's hard to see how it's supposed to be used. Is it redux-promise that adds the .Success ? - jonahe
Yes, I'm using redux-promise, but the .Success comes form API (this api returns object with data/null and Success true/false). So I think the most reasonable thing to do is to use that callback with dataFromRequest that comes back from request in this case. But I guess for my reducer I still need to return the part with type: LOOK_UP, payload: request etc? - Mateusz Szymajda

2 Answers

3
votes

Best way to use another callback in action need to replace you middleware "redux-promise" to "redux-thunk"

import ReduxThunk from 'redux-thunk'
const store = createStore(applyMiddleware(ReduxThunk));

action:

export function lookup(company, callback) {
  const id = company.textfield;
  const url = `${ROOT_URL}${id}`;


  return function(dispatch) { 
        axios.get(url)
       .then((res) => {
            dispatch({ type: LOOK_UP, payload: res,  meta: id });
            callback();
         )
      };
 }

reducers:

import { LOOK_UP } from '../actions/index';

export default function(state = {}, action) {
    switch (action.type) {
        case LOOK_UP:
            const data = action.payload.data;
            const id = action.meta;

            if (data.Success === true) {
                return { ...state, [id]: data.CompanyInformation };
            } else {
                return state;
            }
    }
    return state;

}

2
votes

request is equals to this whole statement:

const request = axios.get(url)
   .then(() => callback())

So if callback() doesn't return something, then the promise will resolve to nothing. I assume you are fetching some data with this call, so you need to pass that data on, or else your reducer will never get it. Like so:

const request = axios.get(url)
   .then((dataFromRequest) => { 
     callback(); 
     return dataFromRequest; 
});

Or you can separate the two and write something like:

const request = axios.get(url);
request.then(() => callback());

return {
    type: LOOK_UP,
    payload: request,
    meta: id
 };

Compare the promises in the snippet below:

const promisedData = () => Promise.resolve(42);

promisedData()
  .then(data => console.log('Resolved data is ', data));


const promisedTrasformedData = () => 
   Promise.resolve(700)
   .then(data => data - 34);
   
   
promisedTrasformedData()
  .then(transformedData => console.log('Transformed data is ', transformedData));
  
  

const promisedAndLostData = () => 
  Promise.resolve(42)
 .then(data => returnNoting())

function returnNoting() { };

promisedAndLostData()
 .then(lostData => console.log('Lost data: ', lostData))