I' trying to filter my redux-saga generator functions api response like this.
function* loadSingleDataAsync(id) {
console.log('Second Saga Works');
let wholeData = [];
let singleData = [];
agent
.get(
`https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest?CMC_PRO_API_KEY=xxx`
)
.then((res) => {
wholeData = [...res.body.data];
singleData = wholeData.filter((currencie) => currencie.id === id);
})
.catch((err) => {
console.log('--------err', err);
});
yield delay(700);
yield put({ type: 'RECEIVE_SINGLE_API_DATA_ASYNC', singleData });
}
After this I'm calling it this way:
export function* rootSaga() {
yield all([takeEvery('RECEIVE_API_DATA', loadDataAsync), takeEvery('RECEIVE_SINGLE_API_DATA', loadSingleDataAsync)]);
}
The first function is working but the second don't,Any suggestions why?
Adding a try, catch inside the saga helps you to know if the saga chain is broken because of any empty responses or data issues.
Related
I have API calls in the following manner:
Call main api which gives me an array of objects.
For each object inside array I have to call another API asynchronously.
As soon as the sub API call for the object is finished, update its data in redux store which is an array (ofc) and show it.
So the scenario is a list showing items which grow in dynamic fashion.
Since I am using redux-saga, I have to dispatch the second part from inside an redux-action. I have tried the follow way:
const response = yield call(get, 'endpoint')
const configHome = response.map(function* (ele) {
const data = yield call(get, ele.SomeURI + '?someParameter=' + ele.someObject.id)
}))
This doesn't work since map doesn't know anything about generator functions. So I tried this:
const response = yield call(get, 'endpoint')
const configHome = yield all(response.map((ele) => {
return call(get, paramsBuilder(undefined, ele.CategoryID))
}))
But this will block my UI from showing available data untill all sub API calls are finished.
I have also tried making a separate generator function which I call from inside map and call its .next() function but the problem here again is that saga doesn't control that generator function so the call effect doesn't return any value properly.
Completely stuck on this part. Would appreciate any help.
Have you tried this, I have created a sample this might help
import { put, takeLatest, all, call } from 'redux-saga/effects';
function* _fetchNews(id) {
const data = yield fetch(
`https://jsonplaceholder.typicode.com/todos/${id}`
).then(function(response) {
const data = response.json();
return data;
});
console.log(id);
yield put({ type: 'NEWS_RECEIVED', data });
return data;
}
function* _getData() {
const json = yield fetch('https://jsonplaceholder.typicode.com/todos').then(
response => response.json()
);
return json;
}
function* fetchNews() {
const json = yield _getData();
const configHome = json.map(ele => _fetchNews(ele.id));
for (var item of configHome) {
yield item;
}
}
function* actionWatcher() {
yield takeLatest('GET_NEWS', fetchNews);
}
export default function* rootSaga() {
yield all([actionWatcher()]);
}
yield all - the generator is blocked until all the effects are resolved or as soon as one is rejected
So you will need to dispatch events separately for each sub API
Let's assume you have 2 actions:
export const getMainApi =() => ({
type: types.GET_MAIN_API,
});
export const getSubApi = endpoint => ({
type: types.GET_SUB_API,
endpoint,
});
Then your operations will be:
const get = endpoint => fetch(endpoint).then(response => response);
function* fetchMainApi(action) {
try {
const response = yield call(get, 'endpoint');
for (let i = 0; i < response.length; i += 1) {
// dispatch here all sub APIs
yield put(
getSubApi(
response[i].SomeURI + '?someParameter=' + response[i].someObject.id,
),
);
}
} catch (e) {
console.log(e);
}
}
function* fetchSubApi(action) {
try {
const response = yield call(get, action.endpoint);
yield put({
type: types.RECEIVE_SUB_API,
response
});
} catch (e) {
console.log(e);
}
}
takeLatest(type.GET_MAIN_API, fetchMainApi)
takeEvery(types.GET_SUB_API, fetchSubApi)
So on success of receiving sub APIs you need to insert data into your state inside reducers.
This is just pseudo code.
I want to fetch api data with redux-saga .
Here is how I call my api :
export const getData = () => {
agent
.get(
"https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest?CMC_PRO_API_KEY=XXX"
)
.then((res) => {
console.log(res.body.data)
})
.catch((err) => {
console.log(err);
});
};
Here is my redux-saga :
import { takeLatest, put } from "redux-saga/effects";
import { delay } from "redux-saga/effects";
function* loadDataAsync() {
yield delay(500);
yield put({ type: "LOAD_DATA_ASYNC" });
}
export function* watchloadData() {
console.log("Im working buddy");
yield takeLatest("LOAD_DATA", loadDataAsync);
}
The problem Is I don't really know how to fetch data through redux-saga,I tried to do research but none of the information seemed to satisfy me.
Could you please give me some suggestions?
The problem Is I don't really know how to fetch data through redux-saga,
No, the problem is you don't really know function generators in javascript.
Anyways if I were to architect that piece of code I would do something like this:
export function* getData(){
yield agent
.get(
"https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest?CMC_PRO_API_KEY=XXX"
)
};
And then:
function* loadDataAsync() {
try {
const { body: { data } } = yield getData();
console.log(data);
yield put({ type: "LOAD_DATA_ASYNC_SUCCESS" });
} catch(err) {
console.log(err);
yield put({ type: "LOAD_DATA_ASYNC_FAILED" });
}
}
I'm trying to fetch data from API In my redux-saga but I'm getting following error from here const {body: {data}} =yield getData() in my redux-saga:
cannot read property body of udefined
Here is my API Function:
export function* getData() {
yield agent
.get(
"https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest?CMC_PRO_API_KEY=xxx"
)
.then((res) => {
getCurrencies(res.body.data);
console.log('--------res.body.data', res.body.data);
setPageCount();
})
.catch((err) => {
console.log(err);
});
}
.then is returning the data itself,So it is not undefined
Here is my redux-saga itself:
function* loadDataAsync() {
console.log("SAGAS WORKS");
yield delay(5000);
try {
const {body: {data}} =yield getData()
console.log(data)
yield put({type:"LOAD_DATA_ASYNC_SUCCESS"});
} catch (err) {
console.log(err)
yield put({type:"LOAD_DATA_ASYNC_ERROR"})
}
}
export function* watchLoadDataAsync() {
yield takeLatest("LOAD_DATA_ASYNC", loadDataAsync);
}
Any solutions please?
You need to use call to get the data back from your asynchronous action
function* loadDataAsync() {
console.log("SAGAS WORKS");
yield delay(5000);
try {
const {body: {}} = yield call(agent.get, "https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest?CMC_PRO_API_KEY=xxx");
console.log(body.data);
// Call your other functions here
getCurrencies(body.data);
setPageCount();
yield put({type:"LOAD_DATA_ASYNC_SUCCESS"});
} catch (err) {
console.log(err)
yield put({type:"LOAD_DATA_ASYNC_ERROR"})
}
}
For more information check out the official docs for call
The await does not seem to work with Redux saga. I need to wait for my API call to finish and then execute the remaining code. What happens now is that AFTER CALL gets printed before the RESPONSE which means await does not seem to work at all. I'm using async calls but not sure what needs to be done extra from the redux saga side?
async componentWillMount() {
console.log("BEFORE CALL")
await this.props.getUserCredit()
console.log("AFTER CALL")
}
mapDispatchToProps = (dispatch) => {
return {
getUserCredit: () => dispatch(getUserCredit()),
}
};
connect(null, mapDispatchToProps)(MyComponent);
Action
export const getUserCredit = () => {
return {
type: GET_USER_CREDIT,
};
};
Redux Saga
const getUserCreditRequest = async () => {
const response = await Api.get(getCreditUrl)
console.log("REPONSE!!!")
console.log(response)
return response
}
function* getUserCredits() {
try {
const response = yield call(getUserCreditRequest);
if (response.status === okStatus) {
yield put({
userCredit: response.data.userCredit
}
));
}
} catch (error) {}
}
export function* getUserCredit() {
yield takeLatest(GET_USER_CREDIT, getUserCredits);
}
export default function* rootSaga() {
yield all([fork(getUserCredit)]);
}
Normally, init / fetching takes place during componentDidMount and don't use async or await inside components. Let the saga middleware do its thing via yield.
// In your component
componentDidMount() { // not async
this.props.getUserCredit(); // dispatch `GET_USER_CREDIT` action
}
mapDispatchToProps = (dispatch) => {
return {
getUserCredit: () => dispatch(getUserCredit()),
}
};
connect(null, mapDispatchToProps)(YourComponent);
You shouldn't be using async/await pattern. As redux-saga handles it by the yield keyword. By the time call is resolved you will have the value available in response.
in actions.js, you should have an action that will carry your data to your reducer:
export function getUserCredits(userCredit) {
return {
type: types.GET_USER_CREDIT_SUCCESS,
payload: userCredit
};
}
Your saga should handle the API call like so:
function* getUserCredits() {
try {
const response = yield axios.get(getCreditUrl); <--- This should work
// No need for if here, the saga will catch an error if the previous call failed
yield put(actions.getUserCredits(response.data.userCredit));
} catch (error) {
console.log(error);
}
}
EDIT: example of using axios with redux-saga
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));
}
}