Waiting for call to finish and then dispatch action in saga - reactjs

I want to make call to server and then use that data for dispatch of other action.
export function* function1(actions) {
console.log('inside');
try {
console.log('getting past orders list');
const url = `/api/getOrders`;
let reqsData = {
order_id: actions.payload.order_id
};
const data = yield call(request, { url, method: 'POST', data:reqsData })
console.log(data);
console.log('///////////////////////////////////');
if (!data.error) {
console.log(data)
yield put({ type: 'nowThis', payload: actions.payload.data });
} else {
console.log('---------------------------------')
console.log('got some error');
}
} catch (error) {
console.log(error)
}
}
But It is not running code next to line
const data = yield call(request, { url, method: 'POST', data:reqsData })
I have similar code before which is running properly + i checked the network and i am getting response 200 for this line.
I have used fork in place of call but it run my code next to that line before the call is complete.

yield call takes function and arguments. Write a method to make a service call. U can use axios npm package (axios.get('../url',params:{params})) and call that function in yield call.
yield call(methodToCallApi(),params,to,method). also, it is better if you keep services calls in a seperate file and just call those methods in saga, instead of defining directly in saga.

It seems your request method is not returning properly. Wrap that in a Promise:
request() {
return new Promise(resolve => {
myApiCall().then(response => {
resolve(response);
}).catch(e => {
reject(e);
});
});
}
and then in your saga, you can yield as normal:
const data = yield call(request, { url, method: 'POST', data:reqsData })

Related

React Redux action doesn't do anything

In Redux I created another action just copying a previous working one but it doesn't work.
The one that works:
export const addEntry = entry => {
const config = {
headers: {
"Content-Type": "application/json",
},
}
return async dispatch => {
const response = await axios
.post("http://localhost:5000/api/db/addentry", entry, config)
.then(results => results.data)
try {
await dispatch({ type: ADD_ENTRY, payload: response })
} catch (error) {
console.log("await error", error)
}
}
}
The one that doesn't:
export const deleteEntry = itemId => {
console.log("action delete 1") // step 1
return async dispatch => {
console.log("action delete 2") // step 2
const response = await axios.delete(
`http://localhost:5000/api/db/deleteitem/${itemId}`,
itemId
)
try {
console.log("action delete 3") // step 3
await dispatch({ type: DELETE_ENTRY, payload: response })
} catch (error) {
console.log("await error", error)
}
}
}
If I log it step by step it stops after the first log
It doens't do anything. The same function addEntry works perfectly. Any idea?
You have to delete the ItemId, using axios.delete instead of axios.post
You also have to put your DELETE_ENTRY like 'DELETE_ENTRY' or else your reducer won't understand it
The reason why the first log comes through, is because that is part of the action creator. Normally, an action creator returns an object in the shape of { type: <type>, payload: <payload> }, but in this case, it returns a new function, which is called a "thunk".
How does this action get dispatched?
In order for this to work, you have to include the redux-thunk middleware, which if it receives a function through dispatch(...), executes that function.
Read up about thunks here
thanks to #HMR I just forgot to dispatch(deleteEntry(item.itemId)) when I call the action

How to deal with errors using all effect in redux-saga

I'm using redux-saga to start multiple requests concurrently as described in the redux-saga docs. The all effect has an all or nothing semantics, similar to Promise.all.
Only if all effects succeed, yield all([...]) succeeds. However, I am doing several requests from which I expect some of them to fail and some of them to succeed. I would like to start all of them in parallel and consume the responses from those requests that have succeeded.
Therefore, I tried to wrap the request into a Promise that always resolves no matter whether the request was successful or not:
// watcher saga
export function* watchMultipleRequests() {
while(true) {
const {ids} = yield take('VIDEOS_REQUEST');
yield fork(doMultipleRequests, ids);
}
}
// worker saga
export function* doMultipleRequests(ids) {
const requests = ids.map(id => {
// api.buildVideoRequest returns a promise once it is invoked
const wrapper = ignoreErrors(api.buildVideoRequest, id);
return call(wrapper);
});
try {
const responses = yield all(requests);
yield put({type: 'VIDEOS_SUCCESS', responses});
} catch (error) {
// should not happen because we are always resolving the promise
console.log(error);
}
};
export function ignoreErrors(fn, ...args) {
return function* () {
yield new Promise(function (resolve) {
return fn(...args)
.then(response => {
console.log('success = ', response);
resolve(response);
})
.catch(response => {
console.log('error = ', response);
resolve(response);
});
});
}
}
I would like to handle the error cases in the reducer. However, if I fire n requests, the responses array contains n times undefined. Has anyone a clue on why this is not working?
The issue is that the ignoreErros function is a generator function.
Implementing it like this:
export function ignoreErrors(fn, ...args) {
return () => {
const ignoreErrorCallback = (response) => response;
return fn(...args).then(ignoreErrorCallback, ignoreErrorCallback);
};
}
is sufficient.

Making ajax call using redux-saga and updating store

im new to whole React env and im trying to create a GET request to Google Api using redux-saga library.
Im sort of missing 2 things. First problem is that my saga function is called again and again forever ( have no idea why ).
The second thing is how to pass the data properly to the reducer.
Here is my Saga:
function* watchAutoCompleteFetch() {
yield takeLatest(UPDATE_ZIP_AUTOCOMPLETE, requestAutoComplete);
}
function requestAutoCompleteApi() {
return fetch(
'some-url-here'
).then((response) => response.json())
.then((json) => json)
.catch((e) => {
put({type: "AUTOCOMPLETE_ZIP_FETCH_FAILED", message: e.message});
});
}
function* requestAutoComplete() {
const data = call(requestAutoCompleteApi);
yield put(updateZipAutoCompleteAction(data));
}
And reducer function:
const updateZipAutoComplete = (state, data) => {
debugger;
return state;
};
In reducer, I get the data as some sort of call object from the redux-saga, not a promise, nor the data.
Any ideas what im doing wrong?
So, turns out there was indeed 2 problems. One is that I was missing yield keyword, the second was I was calling always the same reducer, which triggered the dispatch event again and it went into loop and run forever.
The actual solutions looks like this:
function* watchAutoCompleteFetch() {
yield takeLatest(UPDATE_ZIP_AUTOCOMPLETE, requestAutoComplete);
}
function requestAutoCompleteApi() {
return fetch(
'some-url-here'
).then((response) => response.json())
.catch((e) => {
console.log(e)
});
}
function* requestAutoComplete() {
const data = yield call(requestAutoCompleteApi);
if(data.status ==="OK") {
yield put(updateZipAutoCompleteSucceedAction(data));
} else {
yield put(updateZipAutoCompleteFailedAction(data));
}
}

How to return data using Fetch API to a calling function?

I am experimenting with redux saga, I have an issue trying to fetch data from my API. I changed my API and I am using fetchjs and I ran into some trouble.
The issue is the data is not being returned to the calling function but the api is receiving data.
Here is my Saga code:
/* eslint-disable no-constant-condition */
import { put, call, takeEvery } from 'redux-saga';
import { delay } from 'redux-saga';
import RPC from "../../RPC";
export function* load_contacts() {
console.log("Called"); // This part is printed
try{
const data = yield call(RPC.execute("contact","search_read_path",[[],["name","picture"]],{})); // *this is the calling function. nothing is returned.
console.log("waiting for data",data); // this is not printed
yield put({type:"CONTACTS_LOADED",payload:data});
}
catch (e) {
yield put({type: "CONTACTS_LOAD_FAILED", message: e.message});
}
}
export default function* rootSaga() {
console.log(">> rootSaga");
yield takeEvery('LOAD_CONTACTS', load_contacts)
}
Here is my API code:
module.exports.execute=function(model,method,args,opts,cb) {
console.info("rpc.execute",model,method,args,opts);
if (!_rpc_base_url) throw "RPC base url is undefined";
var params=[model,method];
params.push(args);
if (opts) {
params.push(opts);
}
var headers={
"Accept": "application/json",
"Content-Type": "application/json",
};
if (_database) headers["X-Database"]=_database;
if (_schema) headers["X-Schema"]=_schema;
fetch(_rpc_base_url+"/json_rpc",{
method: "POST",
headers: headers,
body: JSON.stringify({
id: (new Date()).getTime(),
method: "execute",
params: params
}),
})
.then((response) => response.json())
.then((data)=>{
console.log(data); // data is printed here
return data} // doesnot return
)
.catch((err) => {
alert("upload error: "+err);
if (result_cb) result_cb(err,null);
})
};
I believe it has something to do with I am using fetch js (incorrect way). I could not find any solutions that could help.
UPDATE if using callback,
export function* load_contacts() {
console.log("Called")
try{
const data ={};
yield call(RPC.execute("contact","search_read_path",[[],["name","picture"]],{},(err,data)=>{
if (err){
alert(err);
return
}
console.log("data inside is ",data)
data = data
}));
console.log("waiting for data",data)
yield put({type:"CONTACTS_LOADED",payload:data}); // this is called before the data is fetched causing error
}
catch (e) {
yield put({type: "CONTACTS_LOAD_FAILED", message: e.message});
}
}
RPC:
.then((response) => response.json())
.then((data)=>{
if (data.error) {
if (cb) cb(data.error.message,null);
} else {
//console.table(data.result);
if (cb) cb(null,data.result);
}
})
If I do this, the data is returned but I get some exception by yield put
bundle.js:17692 uncaught at rootSaga
at takeEvery(LOAD_CONTACTS, load_contacts)
at load_contacts
TypeError: (0 , _reduxSaga.put) is not a function
at load_contacts$ (http://localhost:8088/static/bundle.js:50254:47)
at tryCatch (http://localhost:8088/static/bundle.js:93097:40)
at Generator.invoke [as _invoke] (http://localhost:8088/static/bundle.js:93335:22)
at Generator.prototype.(anonymous function) [as next] (http://localhost:8088/static/bundle.js:93149:21)
at next (http://localhost:8088/static/bundle.js:48139:27)
at proc (http://localhost:8088/static/bundle.js:48098:3)
at runForkEffect (http://localhost:8088/static/bundle.js:48374:19)
at runEffect (http://localhost:8088/static/bundle.js:48261:872)
at next (http://localhost:8088/static/bundle.js:48143:9)
at currCb (http://localhost:8088/static/bundle.js:48215:7
I removed the try catch and removed the yield put for exception , but I still get the same errors seems the issue is with the first yield put.
Not sure which version of redux-saga you use but put, call, fork, etc. are now inside a different folder
import { put, call, takeEvery } from 'redux-saga/effects';

Very slow response from action creator React/Redux

I am getting super slow response times (upwards 10 seconds) for a function to be called in my action creator.
export function acceptContract(id) {
return function(dispatch) {
const config = { headers: { authorization: localStorage.getItem('etherToken') } };
const data = { data: id };
axios.put('/pending-contracts/accept',
data,
config
).then( response => {
console.log(response);
getPendingContracts();
})
.catch( response => {
// If the get doesn't work, boot the user out to index.
console.log(response);
});
}
}
I update one of the values of contracts in my DB, and I want redux to then dispatch the new list for the user to show the update on the UI.
Not sure why the getPendingContract() invocation takes so long. I get the response from my backend almost immediately.
export function getPendingContracts() {
return function(dispatch) {
axios.get('/pending-contracts', {
headers: { authorization: localStorage.getItem('etherToken') }
})
.then( response => {
console.log('in getPendingContracts')
return dispatch({
type: PENDING_CONTRACTS_LIST,
payload: response.data.message
});
})
.catch( response => {
// If the get doesn't work, boot the user out to index.
console.log(response);
});
}
}
The issue might be related to how you are calling getPendingContracts from acceptContract. You are just calling the function directly, without dispatching it. As far as i can tell all that would do is return you a function that never gets invoked, not sure how you get a response at all. Change the call to this:
then( response => {
console.log(response);
dispatch(getPendingContracts());
})

Resources