A 'yield' expression is only allowed in a generator body - reactjs

I'm using redux-saga to fetch server api data.
My question is that I'm trying to design following code.
However yield put(get01Action(members)); which is commented out has following Syntax error.
A 'yield' expression is only allowed in a generator body.
I don't know how to manage it.
import '#babel/polyfill';
import { fork, take, put } from 'redux-saga/effects';
import axios from "axios";
export function* rootSaga(){
yield fork(fetch01);
yield fork(fetch02);
}
function* fetch01() {
while (true){
yield take('FETCH01_REQUEST');
axios.get('/api/members')
.then(function (response) {
// handle success
let members = response.data.data;
// yield put(get01Action(members));
})
.catch(function (error) {
// handle error
console.log(error);
})
.finally(function () {
// always executed
});
}
}
function* fetch02() {
....
}
function get01Action(members){
return {
type: 'GET_MEMBERS',
member_id: 0,
members: members
}
}
Please give me some advice.
Thanks.

Because your generator fetch01 is sync but you're waiting an Promise to be resovled.
yield can not be wrapped in other functions other than generators.
You can make the generator async, like this:
export async function* rootSaga(){
yield await fork(fetch01);
yield fork(fetch02);
}
async function* fetch01() {
while (true) {
yield take('FETCH01_REQUEST');
try {
const response = await axios.get('/api/members');
// handle success
let members = response.data.data;
yield put(get01Action(members));
} catch (error) {
// handle error
console.log(error);
} finally {
// always executed
}
}
}

you can use call effect to make axios call and then you will be able to use put.
right now it's not working because you are using yield inside call back of promise.
function* fetch01() {
while (true){
try {
yield take('FETCH01_REQUEST');
const response = yield call(axios.get, '/api/members');
yield put(get01Action(response.data.data))
} catch(err) {
console.error(err)
}
}

Related

How to do fast redirect without missing data with redux saga?

I had a problem when I was redirecting too quickly to another page (these pages would call a different APIs from the server). This leads to Components still rendering while no data is available. My expectation is to delay redirection and only navigate when the data is available. Please check the code, my Saga:
function* fetchLaptops() {
try {
const laptops = yield call(apis.fetchLaptops);
} catch (error) {
console.log(error);
}
}
function* fetchMonitors() {
try {
const monitors = yield call(apis.fetchMonitors);
} catch (error) {
console.log(error);
}
}
function* fetchMouses() {
try {
const mouses = yield call(apis.fetchMouses);
} catch (error) {
console.log(error);
}
}
function* mySaga() {
takeLatest(actions.getLaptops.getLaptopsRequest, fetchLaptops);
takeLatest(actions.getLaptops.getMonitorsRequest, fetchMinitors);
takeLatest(actions.getLaptops.getMousesRequest, fetchMouses);
}
File APIs to fetch data from server, example with laptops:
const fetchLaptops = async () => {
const response = await API.get('/laptops')
return response;
};

Fetch api data in redux-saga

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

Get Data from api in redux-saga reactJS

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

Saga is being called multiple times

How to stop saga being called multiple times. Once i dispatched an action i received the result multiple times. i don't know what i'm missing here.
Saga.js
export function* watchRegisterUser() {
yield takeLatest(REGISTER_USER, registerWithEmailPassword);
}
export function* watchLoginUser() {
const logi = yield takeLatest(LOGIN_USER, loginWithEmailPassword);
}
export function* watchLogoutUser() {
yield takeLatest(LOGOUT_USER, logout);
}
export default function* rootSaga() {
yield all([
fork(watchLoginUser),
fork(watchLogoutUser),
fork(watchRegisterUser)
]);
}
When i put wrong credentials i get the response and notification is displayed, working fine for the first time. But when i change the credentials, i get notifications (multiple times). Even when state
Reference: Same problem here
I'm not sure what am i missing here.
Thanks
Update
function* loginWithEmailPassword({ payload }) {
const { history } = payload;
try {
const response = yield call(loginWithEmailPasswordAsync, payload.user);
console.log("login :", response);
if (response.status >= 200 && response.status < 300) {
const loginUser = yield response.json();
localStorage.setItem("access_token", loginUser.access_token);
yield put(loginUserSuccess(loginUser));
history.push("/");
} else if (response.status === 400) {
yield put({
type: LOGIN_USER_FAILURE,
error: "Email and Password are wrong!"
});
}
} catch (error) {
yield put({ type: LOGIN_USER_FAILURE, error: "Something went wrong!" });
}
}
you could try debounce to get around this, in the example below I debounce repeated actions withing a 500ms window.
function* createAutocompleteLists(action) {
const state = yield select()
if (!state.users.loaded && !state.users.loading) {
yield put(getUsers())
yield take([GET_SUCCESS, GET_FAIL])
}
yield call(parseAutocomplete, state.couchdbSavedSearch.current_share_with, state)
}
function* createAutocompleteForwardLists(action) {
const state = yield select()
if (!state.users.loaded && !state.users.loading) {
yield put(getUsers())
yield take([GET_SUCCESS, GET_FAIL])
}
yield call(parseForwardAutocomplete, state.forward.current_forward_to, state)
}
function* debounceCurrentShareWith () {
yield debounce(500, [GET_SUCCESS, SET_CURRENT_SHARE_WITH, SPACE_SET_CURRENT_SHARE_WITH], createAutocompleteLists)
}
function* debounceCurrentShareWithForward () {
yield debounce(500, [GET_SUCCESS, FORWARD_SET_CURRENT_FORWARD_TO], createAutocompleteForwardLists)
}
export default function* usersSagas() {
yield spawn(debounceCurrentShareWith)
yield spawn(debounceCurrentShareWithForward)
}

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.

Resources