Yield put fired before yield call finished - javascript

I tried to make an API call, but the yield put fired before the yield call finished the execution. Here is my code:
Api.js
function callApi(endpoint, token = null) {
const fullUrl =
endpoint.indexOf(API_ROOT) === -1 ? API_ROOT + endpoint : endpoint;
return axios
.get(fullUrl, { headers: { Authorization: token } })
.then(resp => {
return Object.assign([], resp.data);
})
.catch(error => ({ error: error.message || "Something bad happened" }));
}
export const checkOpenRegister = (branchId, userId, token) => {
console.log("in check open");
callApi(
`Branches/${branchId}/registers?filter[where][status]=1&filter[where][userId]=${userId}`,
token
);
};
and in my saga index.js
function* doCheckAuthInfo() {
try {
const user = yield select(getUser);
if (user.token) {
yield put({
type: CHECK_AUTH_INFO_SUCCEED,
payload: { token: user.token }
});
yield put({ type: CHECK_OPEN_REGISTER_REQUESTED });
} else {
//redirect to login
yield put(NavigationActions.navigate({ routeName: "Login" }));
}
} catch (error) {
yield put({ type: CHECK_AUTH_INFO_FAILED, error });
}
}
function* doCheckOpenRegister() {
try {
const user = yield select(getUser);
const response = yield call(
checkOpenRegister,
user.branchId,
user.userId,
user.token
);
yield put({ type: CHECK_OPEN_REGISTER_SUCCEED, payload: response });
} catch (error) {
yield put({ type: CHECK_OPEN_REGISTER_FAILED, error: error.message });
}
}
function* watchCheckAuthInfo() {
yield takeLatest(CHECK_AUTH_INFO_REQUESTED, doCheckAuthInfo);
}
function* watchCheckOpenRegister() {
yield takeLatest(CHECK_OPEN_REGISTER_REQUESTED, doCheckOpenRegister);
}
// use them in parallel
export default function* rootSaga() {
yield all([
fork(watchCheckAuthInfo),
fork(watchCheckOpenRegister)
]);
}
In my saga, on function doCheckOpenRegister, yield PUT fired with no payload but I can find the payload in my network debugger.
I need the payload to trigger action to redirect. In this case, when there is a value in response I need to redirect to main page.

you forgot return Promise from function checkOpenRegister
export const checkOpenRegister = (branchId, userId, token) => {
console.log("in check open");
return callApi(
`Branches/${branchId}/registers?filter[where][status]=1&filter[where][userId]=${userId}`,
token
);
};

Related

Yield call not executing or?

I'm trying to fetch some data from a endpoint. But when i console.log my response i get undefined. And when I run console.log(g.next().value) I get this in the console {##redux-saga/IO: true, combinator: false, type: "CALL", payload: {…}}. Is the Yield call not executing or?
export function* g2(filterKey: string, filterValue: string) {
try {
const response: Response = yield call(fetchEnglishFilterValue, appSettings.language, filterKey, filterValue);
console.log("response: ", response);
const data: string = yield call([response, response.json]);
console.log("data: ", data);
return data;
} catch (e) {
console.log(e)
}
}

how can i use async await in useEffect when i use reactnative?

When useEffect is executed, I want to get the token through AsyncStorage, then get the data value through the axios.post ('/auth/me') router and execute the KAKAOLOG_IN_REQUEST action with disaptch.
As a result of checking the data value with console.log, the data value came in well. But when I run my code, this error occurs.
Possible Unhandled Promise Rejection (id: 1):
Error: Actions may not have an undefined "type" property. Have you misspelled a constant?
Error: Actions may not have an undefined "type" property. Have you misspelled a constant?
how can i fix my code?....
this is my code
(index.js)
const App = ({}) => {
const dispatch = useDispatch();
useEffect(() => {
async function fetchAndSetUser() {
const token = await AsyncStorage.getItem('tokenstore', (err, result) => {
});
var {data} = await axios.post(
'/auth/me',
{},
{
headers: {Authorization: `Bearer ${token}`},
},
);
console.log("data:",data);
dispatch({
type: KAKAOLOG_IN_REQUEST,
data: data,
});
}
fetchAndSetUser();
}, []);
return <Navigator />;
};
export {App};
(reducer/user.js)
import {
KAKAOLOG_IN_FAILURE,
KAKAOLOG_IN_REQUEST,
KAKAOLOG_IN_SUCCESS,
} from '../reducers/user';
function* watchkakaoLogIn() {
yield takeLatest(KAKAOLOG_IN_REQUEST, kakaologIn);
}
function* kakaologIn(action) {
try {
// const result = yield call(kakaologInAPI, action.data);
yield put({
type: KAKAOLOG_IN_SUCCESS,
data: action.data,
});
} catch (err) {
console.error(err);
yield put({
type: KAKAOLOG_IN_FAILURE,
error: err.response.data,
});
}
}
export default function* userSaga() {
yield all([
fork(watchkakaoLogIn),
]);
}
(reducer/index.js)
import { combineReducers } from 'redux';
import user from './user';
import post from './post';
// (이전상태, 액션) => 다음상태
const rootReducer = (state, action) => {
switch (action.type) {
// case HYDRATE:
// // console.log('HYDRATE', action);
// return action.payload;
default: {
const combinedReducer = combineReducers({
user,
post,
});
return combinedReducer(state, action);
}
}
};
export default rootReducer;
(src/index.js)
import {KAKAOLOG_IN_REQUEST} from '../sagas/user';
const App = ({}) => {
const dispatch = useDispatch();
useEffect(() => {
async function fetchAndSetUser() {
try {
const token = await AsyncStorage.getItem('tokenstore');
const {data} = await axios.post(
'/auth/me',
{},
{
headers: {Authorization: `Bearer ${token}`},
},
);
console.log('data::::::', data);
dispatch({
type: 'KAKAOLOG_IN_REQUEST',
data: data,
});
} catch (error) {
}
}
fetchAndSetUser();
}, []);
return <Navigator />;
};
export {App};
Issue
The error message is saying your code can throw an error and it isn't handled. It is also saying that KAKAOLOG_IN_REQUEST is undefined for some reason (perhaps you forgot to import it, or it is really a string).
Solution
Surround your asynchronous code in a try/catch. Define KAKAOLOG_IN_REQUEST or pass as a string "KAKAOLOG_IN_REQUEST".
useEffect(() => {
async function fetchAndSetUser() {
try {
const token = await AsyncStorage.getItem('tokenstore');
const {data} = await axios.post(
'/auth/me',
{},
{
headers: { Authorization: `Bearer ${token}` },
},
);
console.log("data:",data);
dispatch({
type: 'KAKAOLOG_IN_REQUEST',
data: data,
});
} catch(error) {
// handle error, logging, etc...
}
}
fetchAndSetUser();
}, []);

How to test function that call api with jest and redux saga

I need to test redux saga with jest. But it's not working. Here is what I tried
service.js
class Login {
login = async (user: any) => {
try {
const response = await axios({
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
url: `${API.BASE_URL}signIn`,
data: JSON.stringify({
LoginID: user.LoginID,
Password: user.Password,
}),
});
return Bluebird.resolve(response.data);
} catch (err) {
return Bluebird.reject(err);
}
}
}
export default new Login();
worker.js
export function* loginWorker(action) {
try {
const response = yield call(service.login, action.payload);
if (response.Success) {
yield put({type: actionTypes.LOGIN_SUCCESS_ACTION, response});
} else {
toastr.showToast(response.Message);
yield put({type: 'LOGIN_FAILURE', response});
}
} catch (err) {
// dispatch a failure action to the store with the error
yield put({type: 'LOGIN_FAILURE', err});
toastr.showToast(err);
}
}
test.js
describe('test login', () => {
const action = {
type: actionTypes.LOGIN_ACTION,
payload: user,
};
it('login', () => {
const gen = loginWorker(action);
expect(gen.next().value).toEqual(call(service.login, action.payload));
expect(gen.next().value).toEqual(put({type: actionTypes.LOGIN_SUCCESS_ACTION, response}));
});
});
But this function service.login not to be called and I get this error
Cannot read property 'Success' of undefined
Where is my wrong? Please help

rick & morty api problem with request on character with an id

I would like to make a request about the api rick and morty. To get the details of a character, you have to specify an id as a parameter:"https://rickandmortyapi.com/api/character/id"
I use redux-saga to fetch my request, but the request fails with a message:
" Hey! that parameter is not allowed, try with a number instead;)"
I tried the request with the id received in my component and the request is effective. I think it comes either from my action or my saga.......
here is my action :
export const getCharactersDetails = () => {
return { type: GET_CHARACTER_DETAILS }
}
here is my saga :
const getLatestDetails = (id) =>
fetch('https://rickandmortyapi.com/api/character/' + id);
export function* fetchCharacterDetails() {
try {
let myCharacter;
const response = yield call(getLatestDetails);
const result = yield response.json();
if (result.error) {
yield put({ type: DETAILS_REQUEST_FAILED, error: result.error });
} else {
myCharacters = result.results
yield put({ type: DETAILS_RECEIVED, character: myCharacter });
}
} catch (error) {
yield put({ type: DETAILS_REQUEST_FAILED, error: error.message });
}
}
function* actionWatcher() {
yield takeLatest(GET_CHARACTERS, fetchCharacters)
yield takeLatest(GET_CHARACTER_DETAILS, fetchCharacterDetails)
}
and i call my action like that:
let CharacterId = this.props.navigation.state.params.id
this.props.dispatch(getCharactersDetails(CharacterId))
any idea what I'm not doing right?
You are not passing any ID to your getLatestDetails function.
Add the ID to the action you are creating:
export const getCharactersDetails = id => {
return { type: GET_CHARACTER_DETAILS, id }
}
…then add the ID from the action as parameter to your saga and pass it as second argument to your call effect:
export function* fetchCharacterDetails({ id }) {
try {
let myCharacter;
const response = yield call(getLatestDetails, id);
const result = response.json();
if (result.error) {
yield put({ type: DETAILS_REQUEST_FAILED, error: result.error });
} else {
myCharacters = result.results
yield put({ type: DETAILS_RECEIVED, character: myCharacter });
}
} catch (error) {
yield put({ type: DETAILS_REQUEST_FAILED, error: error.message });
}
}
Additional explanation:
The saga gets the action as argument; we are using destructuring here to get just the ID (note the curly braces)
call accepts the function to be called as first parameter, followed by additional arguments passed to the function, this is how getLatestDetails gets the ID
I removed yield before response.json(), this is just a plain old synchronous operation

redux-saga doesn't work at the first time

I have a redux-saga as below:
export function* loadApplianceSaga() {
try {
let {request, success, error} = yield take(ActionTypes.APPLIANCE.LOAD);
request.url = yield select(getResourceLink, request.resource);
const response = yield call(makeRequest, request);
if (!response.error) {
yield put({type: success, payload: response.body});
} else {
yield put({type: error, payload: response});
}
} catch (e) {
yield put({type: ActionTypes.REQUEST.CALL_ERROR, error: e});
}
}
export function* watchLoadAppliance() {
while (true) {
yield* takeEvery(ActionTypes.APPLIANCE.LOAD, loadApplianceSaga);
}
}
and root saga:
export default function* rootSaga() {
yield [
fork(watchLoadAppliance)
]
}
I'm facing a problem that loadApplianceSaga doesn't work at the first time. I logged and saw that in the first time it only dispatched ActionTypes.APPLIANCE.LOAD action then no action is dispatched. But in the second time, I can see success action or failed action which are dispatched.
Could anyone tell me what was wrong? Thanks in advance!
Updated action:
export const loadAppliances = () => {
return {
type: ActionTypes.APPLIANCE.LOAD,
request: {
resource: Resources.Appliances,
param: {
page: 0,
size: 5,
sort: 'name,desc'
},
header: {
Accept: 'application/json'
}
},
success: ActionTypes.APPLIANCE.LOAD_SUCCESS,
error: ActionTypes.APPLIANCE.LOAD_ERROR
}
};
you have used take two times.
try
export function* loadApplianceSaga(action) {
try {
let {request, success, error} = action;
request.url = yield select(getResourceLink, request.resource);
const response = yield call(makeRequest, request);
if (!response.error) {
yield put({type: success, payload: response.body});
} else {
yield put({type: error, payload: response});
}
} catch (e) {
yield put({type: ActionTypes.REQUEST.CALL_ERROR, error: e});
}
}
export function* watchLoadAppliance() {
while (true) {
yield* takeEvery(ActionTypes.APPLIANCE.LOAD, loadApplianceSaga);
}
}

Categories