Silver
Silver

Reputation: 67

How to know an asynchronous call has finished with redux-saga?

on a button click I dispatch an action to make a get request , I wanna wait for the request to complete before I continue ,whats the right approach to do this? I do dispatch an action based on the result in my saga:

function* workerSaga() {
try {
  const response = yield call(fetchCall);
  const myData= response.data;

  // dispatch a success action to the store 
  yield put({ type: "API_CALL_SUCCESS", myData});

} catch (error) {
  // dispatch a failure action to the store with the error
  yield put({ type: "API_CALL_FAILURE", error });
}

But how do I know in the code that did dispatch the request action, that the get request has finished?

thx in advance.

EDIT:

without saga a request would look similar to :

    axios.get(myURL)
    .then(/*here we know the request has finished*/);

with saga:

this.props.dispatch({type:"API_CALL"})
//here i would like to know the request has finished

Upvotes: 0

Views: 3015

Answers (2)

Nicholas Tower
Nicholas Tower

Reputation: 85012

this.props.dispatch({type:"API_CALL"})
//here i would like to know the request has finished

In general, that's not the way redux saga gets used by a component. Instead, your component should be subscribed to the store using react-redux's connect method, so when API_CALL_SUCCESS goes through the reducer and the state gets updated, your component will get new props and thus rerenders. If necessary, you can implement a componentDidUpdate to run some code when the prop changes.

It is possible to do a work around so that you can find out when the work is complete through a promise, but doing so requires introducing more coupling between your component and your saga, so i recommend not to use this for most cases. You can set up your action so that part of its payload is a callback function, and then have the saga call that function. For example:

// In the saga
function* workerSaga(action) {
  try {
    const response = yield call(fetchCall);
    const myData = response.data;

    // dispatch a success action to the store 
    yield put({ type: "API_CALL_SUCCESS", myData});
    if (action.callback) {
      action.callback(myData);
    } 

  } catch (error) {
    // dispatch a failure action to the store with the error
    yield put({ type: "API_CALL_FAILURE", error });
  }
}

// in the component
componentDidMount() {
  this.props.dispatch({ type: 'API_CALL', callback: onFetchSuccess }); 
}

onFetchSuccess(data) {

}

Or if you want to have a promise, then the callback could be the resolver for a new promise:

componentDidMount() {
  const promise = new Promise((resolve) => {
    this.props.dispatch({ type: 'API_CALL', callback: resolve }); 
  });
  promise.then(data => {

  });
}

// or, with async/await:
async componentDidMount() {
  const data = await new Promise(resolve => 
    this.props.dispatch({ type: 'API_CALL', callback: resolve })
  );
}

Upvotes: 3

Kamran Nazir
Kamran Nazir

Reputation: 692

Here's an example using try/catch

function* workerSaga() {

  try {
    const response = yield call(fetchCall);
    if (response.data) { // check based on what the your API returns
      const myData= response.data;
      // dispatch a success action to the store 
      yield put({ type: "API_CALL_SUCCESS", myData});
    } else {
      throw response;
    }

  } catch (error) {
    // dispatch a failure action to the store with the error
    yield put({ type: "API_CALL_FAILURE", error });
  }
}

without try/catch

function* workerSaga() {
  const response = yield call(fetchCall);

  if (response.data) { // check based on what the your API returns
    const myData= response.data;

    // dispatch a success action to the store 
    yield put({ type: "API_CALL_SUCCESS", myData});
  } else {
    // dispatch a failure action to the store with the error
    yield put({ type: "API_CALL_FAILURE", error });
  }
}

Upvotes: 0

Related Questions