Yield call returning undefined - call

Anybody can help me what's wrong with my code. I trying to get data from API in sagas file like this. I've try to console.log the response.data in read function and I get the data (array). But when it assigned in variable (data) in generator function it has undefined.
// sagas.js
const PATH = 'product';
const read = async (path) => {
await request.get(path)
.then(response => {
console.log('response from read :', response.data)
return response.data;
})
.catch(err => { throw err })
}
function* loadProduct() {
try {
const data = yield call(read, PATH);
yield put(actions.loadProductSuccess(data));
}
catch (error) {
console.log('what's the error :', error)
yield put(actions.loadProductFail());
}
}
export default function* rootSaga() {
yield all([
takeEvery('LOAD_PRODUCT', loadProduct)
]);
}

Thanks a lot, I got my problem.
I forgot about the curly brace, I change my code like this:
const PATH = 'product';
const read = async (path) =>
await request.get(path)
.then(response => {
console.log('response from read :', response.data)
return response.data;
})
.catch(err => { throw err })
...
it works properly!

You need to return await request.get(path), then you will get response.data in your saga functions.

Related

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

Fetch array of url's

I'm working with redux and I am trying to fetch Star War API.
Here is my code:
import { MOVIES_ERROR, MOVIE_CHARACTERS } from "./types";
// Get all characters
export const getCharacters = (userId) => async (dispatch) => {
try {
const res = await fetch(`https://swapi.dev/api/films/${userId}`);
if (!res.ok) {
throw new Error("sometheing went wrong");
}
const getData = await res.json();
const characters = await getData.characters;
let people = [];
Promise.all(
characters.map((url) =>
fetch(url)
.then((response) => response.json())
.then((name) => people.push(name))
)
);
dispatch({
type: MOVIE_CHARACTERS,
payload: people,
});
} catch (err) {
dispatch({
type: MOVIES_ERROR,
payload: { msg: err.response.statusText, status: err.response.status },
});
}
};
when I make a console log inside a promise. all I got people array filled with all the data, but when I dispatch it I got an empty array in the reducer. can anyone tell me what the mistake that i did?
I got the problem just now, I need to add await before Promise.all :)

Failing to integrate Redux-saga with Redux

I am trying to use Redux-saga for the first time, so I have the following sagas file:
backendAuth.js
import {all, call, fork, put, takeEvery} from "redux-saga/effects";
import {auth} from 'backend/backend';
import {
SIGNIN_USER,
SIGNOUT_USER,
SIGNUP_USER
} from "constants/ActionTypes";
import {showAuthMessage, userSignInSuccess, userSignOutSuccess, userSignUpSuccess} from "actions/Auth";
const createUserWithUsernamePasswordRequest = async (username, password) =>
await auth.createUserWithUsernameAndPassword(username, password)
.then(authUser => {
console.log('authUser: '+authUser);
return authUser;
})
.catch(error => error);
const signInUserWithUsernamePasswordRequest = async (username, password) =>
await auth.signInWithUsernameAndPassword(username, password)
.then(authUser => authUser)
.catch(error => error);
const signOutRequest = async () =>
await auth.signOut()
.then(authUser => authUser)
.catch(error => error);
function* createUserWithUsernamePassword({payload}) {
const {username, email, password} = payload;
try {
const signUpUser = yield call(createUserWithUsernamePasswordRequest, username, email, password);
if (signUpUser.message) {
yield put(showAuthMessage(signUpUser.message));
} else {
localStorage.setItem('user_id', signUpUser.user.uid);
yield put(userSignUpSuccess(signUpUser.user.uid));
}
} catch (error) {
yield put(showAuthMessage(error));
}
}
function* signInUserWithUsernamePassword({payload}) {
const {username, password} = payload;
try {
const signInUser = yield call(signInUserWithUsernamePasswordRequest, username, password);
if (signInUser.message) {
yield put(showAuthMessage(signInUser.message));
} else {
localStorage.setItem('user_id', signInUser.user.uid);
yield put(userSignInSuccess(signInUser.user.uid));
}
} catch (error) {
yield put(showAuthMessage(error));
}
}
function* signOut() {
try {
const signOutUser = yield call(signOutRequest);
if (signOutUser === undefined) {
localStorage.removeItem('user_id');
yield put(userSignOutSuccess(signOutUser));
} else {
yield put(showAuthMessage(signOutUser.message));
}
} catch (error) {
yield put(showAuthMessage(error));
}
}
export function* createUserAccount() {
yield takeEvery(SIGNUP_USER, createUserWithUsernamePassword);
}
export function* signInUser() {
yield takeEvery(SIGNIN_USER, signInUserWithUsernamePassword);
}
export function* signOutUser() {
yield takeEvery(SIGNOUT_USER, signOut);
}
export default function* rootSaga() {
yield all([
fork(signInUser),
fork(createUserAccount),
fork(signOutUser)
]);
}
And this is the file where the asynchronous consult to an api is performed:
backend.js
import axios from 'axios';
const backendServer = 'http://localhost:8000/';
const signInEndpoint = backendServer + 'api/token_auth/';
const signInWithUsernameAndPassword = (username, password) => {
axios.post(backendServer+"api/token_auth/", {
username: username,
password: password
})
.then(Response => {
console.log('Response: '+Response)
return Response;
})
.catch(Error => Error);
}
export const auth = {
signInWithUsernameAndPassword: signInWithUsernameAndPassword
}
The ajax is well executed through axios, and the console.log() in backend.js is reached, but the console.log() in backendAuth is not, and I get the following error in the console:
index.js:1375 Invariant Violation: Objects are not valid as a React child (found: TypeError: Cannot read property 'then' of undefined). If you meant to render a collection of children, use an array instead.
I believe the problem lies in the way I am defining the return of the value of the then in the ajax of backend.js, but I am pretty new to frontend development, so I am not sure about it.
The root cause is a simple fix - you are using an explicit block for the body of your signInWithUsernameAndPassword arrow function, so you need to use an explicit return. Since you have no return statement that function returns undefined which is cascading to cause the overall issue.
The quick fix is just to update the function to:
const signInWithUsernameAndPassword = (username, password) => {
return axios.post(backendServer+"api/token_auth/", {
username: username,
password: password
})
.then(Response => {
console.log('Response: '+Response)
return Response;
})
.catch(Error => Error);
}
Now to understand the final error you're seeing due at the moment: since signInWithUsernameAndPassword returns undefined, you eventually enter the catch block of signInWithUsernamePassword in backendAuth.js due to the type error of calling undefined.then in signInUserWithUsernamePasswordRequest. The error in that catch block is an Error instance, not a plain string. I'm assume you're then displaying the error that is passed to showAuthError somewhere in a react component tree, but an Error instance is not something that is valid to be rendered. You could try calling toString() on it first to display future errors instead of causing a rendering error.

Why cannot catch error in redux-saga generator?

Here is store/store.js
...
const initSagaMiddleware = createSagaMiddleware();
const middlewares = [initSagaMiddleware, fetchPhotosMiddleware, putPhotoMiddleware];
const middlewareEnhancer = applyMiddleware(...middlewares);
...
initSagaMiddleware.run(rootSaga);
export default store;
sagas/api-saga.js
export default function* rootSaga() {
yield all([
photoWatcher()
]);
}
function* photoWatcher() {
yield takeEvery(PUT_PHOTO, putPhotoWorker);
}
function* putPhotoWorker(action) {
try {
const payload = yield call(putPhoto, action.urlParams, action.body);
yield put({ type: PHOTO_UPDATED, payload });
} catch (err) {
yield put({ type: API_ERROR_PUT_PHOTO, payload: err });
}
}
and services/api.js
export function putPhoto(urlParams, body) {
return axios.put('/abc/' + urlParams.key + '/def/' + urlParams.id + '/edit', body)
.then(res => {
return res.data;
})
.catch(err => err);
}
My problem is: even when I get error from an api call, redux-saga putting PHOTO_UPDATED instead of API_ERROR_PUT_PHOTO. What am I doing wrong? How to catch error?
The problem is you are trying to catch the same error twice. Just remove the catch form the putPhoto function and it should work.
export function putPhoto(urlParams, body) {
return axios.put('/abc/' + urlParams.key + '/def/' + urlParams.id + '/edit', body)
.then(res => {
return res.data;
});
}
Both then and catch promises in putPhoto func were resulting in try statement. Because of that, I give up on try...catch, instead I'm using if...else statement now. Like this:
function* putPhotoWorker(action) {
const payload = yield call(putPhoto, action.urlParams, action.body);
if (isResponseTypeWhatIExpected) {
yield put({ type: PHOTO_UPDATED, payload });
} else {
yield put({ type: API_ERROR_PUT_PHOTO, payload });
}
}

axios__WEBPACK_IMPORTED_MODULE_0___default.a[method] is not a function

I'm getting above error when I make a request to backend. I have similar code on another project, there is no issue. but here it's causing problems
my code:
import axios from 'axios';
export default function apiCall(method, path, data) {
console.log(method, url, data);
return new Promise((resolve, reject) => {
return axios[method](path, data)
.then(res => {
return resolve(res.data);
})
.catch((err) => {
console.log(err)
reject(err)
});
});
}
api call function
apiCall('POST', `${process.env.REACT_APP_BASE_URL}/`, {standard, subject, totalMarks, totalQuestions} )
.then(data =>{
console.log(data);
})
.catch(err=>{
console.log(err);
return this.props.addError(err.message)
});
Pay attention to the fact that objects in javascript are case sensitive, therefore, accessing obj['post'] and obj['POST'] will return different values.
axios has method get, post etc, as lowercase, you are trying to access them via uppercase, therefore u getting an undefined value.
You can fix that by converting the method variable to lowercase.
import axios from 'axios';
export default function apiCall(method, path, data) {
return new Promise((resolve, reject) => {
return axios[method.toLowerCase()](path, data)
.then(res => {
return resolve(res.data);
})
.catch((err) => {
console.log(err)
reject(err)
});
});
}
BTW, axios methods are already returning Promises, so you can make your code a bit simpler by using it.
import axios from 'axios';
export default function apiCall(method, path, data) {
return axios[method.toLowerCase()](path, data)
.then(res => res.data)
.catch((err) => {
console.log(err);
return Promise.reject(err);
});
}
I had the same issue and felixmosh gave me the key
const { data } = await axios.PUT(
`/api/users/profile/update/`,
user,
config
)
I fixed it changing the method and it worked to me ;)
const { data } = await axios.put(
`/api/users/profile/update/`,
user,
config
)

Resources