using axios in service object in React Native fails - react-native

I can't figure out why this does not work. I think it has to do with having a promise nested inside another promise:
I set up my api service object:
api.js
import axios from 'axios';
import apiConfig from './apiConfig';
import deviceStorage from '../services/deviceStorage.js';
export const get = (endpoint, payload = {}, headers = {}) => {
const jwt = deviceStorage.loadJWT
headers.Authorization = jwt
console.log("running..");
axios({
method: 'GET',
url: apiConfig.development.url + endpoint,
headers: headers,
data: payload,
}).then((response) => {
console.log('will return response..');
return response;
}).catch((error) => {
console.log('will return error..');
return error;
});
};
then I call it from a screen:
NotificationsScreen.js
import React from 'react';
import { View, ScrollView, Text, Button, StyleSheet } from 'react-native';
import axios from 'axios';
import Header from '../components/Header';
import NotificationCardSection from '../components/notificationsScreen/NotificationCardSection';
import NotificationCardList from '../components/notificationsScreen/NotificationCardList';
import { Loading } from '../components/common/';
import globalStyles from '../globalStyles';
import * as api from '../services/api'
export default class NotificationsScreen extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: true,
notifications: [],
error: ''
};
}
componentDidMount() {
console.log("will get data from api");
api.get(
'/notifications'
).then((response) => {
console.log("got back data from api");
this.setState({
notifications: response.data.data,
loading: false
});
}).catch((error) => {
console.log("got error from api");
this.setState({
error: 'Error retrieving data',
loading: false
});
});
}
but i get an error:
TypeError: Cannot read property 'then' of undefined.
terminal shows 'running..' but does not show 'will return response...' or 'will return error' so they are not firing.
I assume it is because the api call has not finished yet, but since it is async, how can I make sure it HAS finished when calling it from the screen?

You are expecting a Promise to be returned from your get since you are using then and catch on it but you are just returning a response or an error.
Your get function should look like the below if you want to use .then with it:
export const get = (endpoint, payload = {}, headers = {}) => {
return new Promise((resolve, reject) => {
const jwt = deviceStorage.loadJWT
headers.Authorization = jwt
console.log("running..");
axios({
method: 'GET',
url: apiConfig.development.url + endpoint,
headers: headers,
data: payload,
})
.then((response) => {
console.log('will return response..');
resolve(response);
})
.catch((error) => {
console.log('will return error..');
reject(error);
});
});
};

Related

Mock .get() Function using Jest on VueJS

I am trying to mock a GET request to get some Posts using the ID. This is the code I am trying to mock:
getPost() {
this.refreshToken();
http
.get(`/posts/${this.$cookie.get('postid')}`, {
headers: {
"Authorization": `Bearer ${this.$cookie.get('token')}`,
"Content-type": "application/json",
},
})
.then((response) => {
this.post = response.data;
})
.catch((error) => {
console.log(error.response);
});
}
This is my attempt at a test:
import {getPost} from '#/views/Post.vue'
import axios from 'axios';
jest.mock('axios');
describe('get Post by ID', () => {
afterEach(() => {
jest.resetAllMocks();
});
it('should return empty when axios.get failed', async () => {
const getError = new Error('error');
axios.get = jest.fn().mockRejectedValue(getError);
const actualValue = await getPost();
expect(actualValue).toEqual(new Map());
expect(axios.get).toBeCalledWith('/posts/postid');
});
it('should return users', async () => {
const mockedUsers = [{ postID: 1 }];
axios.get = jest.fn().mockResolvedValue(mockedUsers);
const actualValue = await getPost(['1']);
expect(actualValue).toEqual(mockedUsers);
expect(axios.get).toBeCalledWith('/posts/postid');
});
})
The error I am getting is:
TypeError: (0 , _Post.getPost) is not a function
I am not sure what to do, and any help would be super appreciated. Thanks!
Assuming you have getPost() defined in the Post component's methods, you can't use named imports to access getPost. Instead, you'll have to mount the component, and use the wrapper's vm:
// Post.spec.js
import { shallowMount } from '#vue/test-utils'
import Post from '#/views/Post.vue'
it('...', () => {
const wrapper = shallowMount(Post)
await wrapper.vm.getPost()
expect(wrapper.vm.post).toEqual(...)
})
Also make sure to return the axios call in getPost() so that it could be awaited:
// Post.vue
export default {
methods: {
getPost() {
this.refreshToken();
👇
return http.get(/*...*/)
.then(/*...*/)
.catch(/*...*/);
}
}
}

axios cancel is not working in react native redux

I am using axios for api call in react native application. I have redux for managing state in the application. I am trying to cancel the axios request when the component is unmounted but it is not cancelling.
import axiosMaker from '../../axios'
useEffect(() => {
const source = axios.CancelToken.source()
props.fetchFile(fileName, source.token)
return() => {
console.log('un mounting document view')
source.cancel()
}
},[])
//redux action
export const fetchFile = (location, token) => {
return async (dispatch) => {
dispatch(fetchFileRequest())
const config = {
path : location,
}
let axiosObj = await axiosMaker()
try{
axiosObj.post('download', config,
{
headers :{
'authorization' : `Bearer ${accessToken}`
}
},
{
cancelToken: token
}
)
.then(async response => {
console.log('doucment view success')
dispatch(fetchFileSuccess(response.data))
})
.catch(error => {
console.log('doucment view failure')
dispatch(fetchFileFailure(error))
})
} catch(error){
if(axiosObj.isCancel(error)){
console.log('request is canceled')
}
console.log('hello world')
}
}
}
What am I doing wrong here? How to cancel the axios call in redux?
I was sending accessToken and cancelToken separately. I sent it together.
axiosObj.post('download', config,
{
headers :{
'authorization' : `Bearer ${accessToken}`
},
cancelToken : token
}
)
and another changes is isCancel() function is available in the axios object itself not in the customize axios object. So, I imported the axios
import axios from 'axios'
and called isCancel function in this object, not in the axiosMaker object.
if(axios.isCancel(error)){
console.log('request is canceled')
}

Action Creator return undefined axios

Can successfully register the user using my action creator but it returns undefined. I think it's the way how am returning my dispatch
axiosInstance
import axios from 'axios';
import AsyncStorage from '#react-native-community/async-storage';
// import base url
import {API_URL} from '../constants';
const instance = axios.create({
baseURL: API_URL,
timeout: 2000,
});
instance.interceptors.request.use(
async(config) => {
const token = await AsyncStorage.getItem('token');
if(token) {
config.headers.Autherization = `${token}`;
}
return config;
},`enter code here`
(err) => {
return Promise.reject(err);
}
)
export default instance;
SignUP Action.
import axiosInstance from '../../api/axiosInstance';
import {REGISTER_USER_SUCCESS, REGISTER_USER_FAIL} from '../actionTypes/index';
const registerSuccess = (payload) => {
return{
type: REGISTER_USER_SUCCESS,
data: payload
}
};
const registerError = (payload) => {
return {
type: REGISTER_USER_FAIL,
data: payload
}
};
export const SignUp = (registerData) => async dispatch => {
axiosInstance.post('/users/register', registerData)
.then((response)=> {
dispatch(registerSuccess(response.data));
})
.catch((error) => {
dispatch(registerError(error));
});
}
Here is how am using my action creator .. the result is undefined . I want to have a check some that I can redirect the screen to another login screen or home screen
SignUP Submit function
dispatch(registerAction.SignUp(values))
.then( (result) => {
console.log('klhadsghaj',result.status);
if(result.success) {
try {
navData.navigation.navigate("Login");
}catch (err) {
console.log(err)
}
} else {
Alert.alert('Registration failed. Try Again')
}
})
.catch(err => console.log(err))

Axios with Auth is not a valid axios instance

I'm trying to set Authorization headers on an axios post request using axios.create(). I have this in a file and when I try to import the function into my React Native component, I get the following error:
axiosWithAuth.default().post is not a function
below is my code for the function:
import axios from 'axios';
import {AsyncStorage} from 'react-native';
// import AsyncStorage from '#react-native-community/async-storage'
async function axiosWithAuth() {
const token = await AsyncStorage.getItem('userToken');
console.log('token from asyncstorage' , token);
return axios.create({headers : {'Content-Type': null, Authorization : token}})
}
export default axiosWithAuth;
and I'm using the function in the following React Native Component:
import axiosWithAuth from '../utils/axiosWithAuth';
const Recipe = (props) => {
const likeIt = () => {
console.log('like pressed');
console.log('props', props.recipe.id);
setLike(!like);
axiosWithAuth().post(`API_URL`,{})
.then(res => console.log('response from post like: ', res.data))
.catch(err => console.log('error in posting like', err.response))
}
return (
<Text>This is the Recipe Component </Text>
)
}
You used async with axiosWithAuth function. It is returning a Promise, not an instance of axios. You have to write it like this
(await axiosWithAuth()).post(`API_URL`,{})
.then(res => console.log('response from post like: ', res.data))
.catch(err => console.log('error in posting like', err.response))
Below is the code that worked. Create a variable and set it = await axiosWithAuth(). Then do a post request off of the variable. Below is the axiosWithAuth function definition:
import axios from 'axios';
import {AsyncStorage} from 'react-native';
async function axiosWithAuth() {
const userToken = await AsyncStorage.getItem('userToken');
console.log('userToken', userToken);
// return axios.create({headers : {'Content-Type': null, Authorization : userToken}})
return axios.create({headers : {Authorization : userToken}})
}
export default axiosWithAuth;
and below is the code that is using the axiosWithAuth function:
import axiosWithAuth from '../utils/axiosWithAuth';
const likeIt = async () => {
await setLike(!like);
console.log('liked?', like);
const axiosAuth = await axiosWithAuth();
console.log('axiosAuth', axiosAuth);
if (!like) {
axiosAuth.post(`https://url`,{})
.then(res => {
console.log('response from post like: ', res.data.message);
})
.catch(err => console.log('error in posting like', err.response))
} else {
axiosAuth.delete(`https://url`)
.then(res => console.log('res from unlike', res))
.catch(err => console.log('err from deleting like', err))
}
}

Redux fetch data from api

I am trying to fetch some data from an api using Redux. My code looks like this:
Action:
// Import libraries
import axios from 'axios';
// Import types
import {
GET_ALL_PICKS
} from './types';
export const getAllPicks = ({ token }) => {
const getPicks = (dispatch) => {
axios({
method: 'get',
url: 'http://myapi/',
headers: {
Authorization: `Bearer ${token}`
}
})
.then((response) => {
console.log(response.data); // First log here returns data just fine
dispatch({
type: GET_ALL_PICKS,
payload: response.data
});
})
.catch((error) => {
console.log(error);
});
};
return getPicks;
};
Reducer:
// Import types
import {
GET_ALL_PICKS
} from '../actions/types';
// Set Initial State
const INITIAL_STATE = {
allPicks: {},
loading: false,
error: ''
};
// Make pick reducers
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case GET_ALL_PICKS:
return { ...state, allPicks: action.payload }; // Logging action.payload here returns data just fine
default:
return state;
}
};
Component:
// Import Libraries
import React, { Component } from 'react';
import { Text } from 'react-native';
import { connect } from 'react-redux';
import {
getAllPicks
} from '../actions/picks';
// Make Component
class HomeScreen extends Component {
// Fetch Data
componentWillMount() {
const { token } = this.props;
this.props.getAllPicks({ token });
}
// Test response
componentDidMount() {
console.log(this.props.allPicks); // This log returns empty object, why?!
}
render() {
return (
<Text>Test</Text>
);
}
}
const mapStateToProps = ({ auth, picks }) => {
const { token } = auth;
const { allPicks } = picks;
return {
token,
allPicks
};
};
export default connect(mapStateToProps, { getAllPicks })(HomeScreen);
When I run the app I see the data in the action console.log and if I run a console.log(action.payload) in the reducer I see the data just fine but in component I see an empty array which suggests I'm not hooking up the data in my reducer correctly? Here's a screen shot of the logs:
I have also tried this in my reducer after some Googling:
return Object.assign({}, state, {
allPicks: action.payload
});
but again I got the same result. Can anyone explain to me what I am doing wrong?
You are confusing the component lifecycle and the API lifecycle.
In practice, what's happening is:
componentWillMount
getAllPicks
componentDidMount (at which point, the API didn't return, the picks are empty)
[... wait for the API to return]
then the API returns with the data, but too late
What you need to do then is check for your "picks" state in the render() function, which will be updated each time your state changes (which happens when the API returns), thanks to the connect() function.
You can also check that the picks are updated properly using componentWillUpdate, not componentDidMount which again has nothing to do with the props being updated.