Cant send AXIOS request from react test - javascript

I hope someone can give me a hint with the following problem.
I am currently working on the frontend for a REST API. I would like to test if I can submit a POST request.
Using the npm test command, the test runs and shows a green tick for this test function. However, no POST request is sent and thus no entry is written to the database.
The function createObject(json) is called correctly during the test and the JSON string passed is also correct. Unfortunately the AXIOS POST method is not called.
When I click on "Post Object" via the browser the AXIOS method is called and an object is created in the database.
PostClient.js
import axios from 'axios';
const options = {
headers: {
// 'Content-Type': 'application/x-www-form-urlencoded'
'Content-Type': 'application/json',
'Accept': 'application/json',
} };
export class PostClient {
// This function is called by the test, but the Axios command is not.
static createObject(json) {
const response = axios.post('http://localhost:8080/object/create/', JSON.stringify(json), options)
.then(response => {
console.log(response.data);
return response;
}).catch(function (error) {
console.log(error);
});
return response;
}
}
App.test.js
describe('Test', function(){
let id;
it('addObject()', function () {
const response = PostClient.createObject(objectJSON);
this.id = response.id;
expect(response.status == 200);
});
});
App.js
class App extends React.Component {
render() {
return (
<>
<h2>createObject</h2>
<div className="createObject">
<button onClick={() => PostClient.createObject(objectJSON)}>Post Object</button>
</div>
...
</>
);
}
}
export default App;

First: read the comment by Yevgen Gorbunkov :)
Second: axios.post() method returns a Promise - you are returning that promise instead of returning the result of your request.
My advice is to review how promises work; MDN has a nice article - and maybe brush up on asynchronous code in general.
The quick and dirty solution might be to turn your function into an async function and use async/await:
static async createObject(json) {
// Note: this might throw an error; try...catch is a good idea
const response = await axios.post(url, payload, options);
return response.data;
}

Related

Cannot render and map POST request array promise

I have an API called getQuote and a component called QuoteCard. Inside QuoteCard I'm trying to render an array of users that liked a quote. The API works fine, I have tested it, and the code below for getting the users works fine too.
const Post = async (url, body) => {
let res = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
"accept": "*/*"
},
body: JSON.stringify(body)
}).then(r => r.json());
return res;
}
const getAllLikes = async () => {
let users = await Post('api/getQuote', {
id: "639e3aff914d4c4f65418a1b"
})
return users
}
console.log(getAllLikes())
The result is working as expected :
However, when trying to map this promise result array to render it onto the page is where I have problems. I try to render like this:
<div>
{getAllLikes().map((user) => (
<p>{user}</p>
))}
</div>
However, I get an error that states:
getAllLikes(...).map is not a function
I don't understand why this is happening. Why can't I map the array? Is it because it's a promise or something?
And if anyone needs to see the getQuote API, here it is:
//Look ma I wrote an API by myself! :D
import clientPromise from "../../lib/mongodb";
const ObjectId = require('mongodb').ObjectId;
import nc from "next-connect";
const app = nc()
app.post(async function getQuote(req, res) {
const client = await clientPromise;
const db = client.db("the-quotes-place");
try {
let quote = await db.collection('quotes').findOne({
_id: new ObjectId(req.body.id)
})
res.status(200).json(JSON.parse(JSON.stringify(quote.likes.by)));
} catch (e) {
res.status(500).json({
message: "Error getting quote",
success: false
})
console.error(e);
}
})
export default app
Thanks for any help!
It is due to the fact that getAllLikes is an async function and thus it returns promise which does not have a map function.
You can either save it in a state variable before using await Or chain it with .then.
Minimal reproducible example which works
const getAllLikes = async () => {
return ['a', 'b']
}
getAllLikes().then((r) => r.map((g) => { console.log(g) }))
Edit: The above code won't work if directly used with jsx since the return of getAllLikes will still be a promise. Solution would be to save it in a state variable and then using it.
I am from Angular and I believe we call pipe on Observables (or Promises). Map can then be called inside the pipe function
observable$ = getAllLikes().pipe(map( user => <p>{user}</p>))
If there is no pipe, I can only think of manually subscribing (which is not a good practice)
sub$ = getAllLikes().subscribe( user => <p>{user}</p>)
// unsub from sub$ appropriately
// We do this from ngOnDestroy in angular
ngOnDestroy() {
this.sub$?.unsubscribe()
}

Nextjs Axios only works on 1st call

I'm using NextJs for my project and when I use Axios instead of fetch there is only my 1st call that's working properly.
Imagine that I'm on my page /home that got a getServerSideProps function. It will work perfectly on the 1st call, the second one will always return undefined, no matter if it's 2xx or 4xx code. If I shut down the dev serv and rerun it, it's working again, but only once....
I'm pretty sure my code is working since I have no problem when using fetch or axios on 1st call but w/e I'll give it to you :
home.js
export async function getServerSideProps(context){
const { id } = context.query
const {produit,error} = await getProduit(id)
return {
props:{
produit:produit??null,
error:error??null
}
}
}
useCase.js
export function getProduit(id){
return getRequest(`produit/${id}`)
.then(response => {
const produit = response.produit
const adapter = new AdapterProduit(produit)
return {produit:JSON.parse(JSON.stringify(adapter.toArrayProduit()))[0]}
})
.catch(err => {
return {error:JSON.parse(JSON.stringify({status:err?.status,message:err?.data?.err}))}
})
}
backFacade.js
const BACK_END_SERVER = 'http://localhost:3000/api'
const axios = require('axios');
export function getRequest(ressource){
const token = '' //localStorage.getItem('token')
return axios.get(`${BACK_END_SERVER}/${ressource}`,
{headers: {'Authorization': 'Bearer ' + token}})
}
//Interceptors
axios.interceptors.response.use(function (response) {
return response.data
}, function (error) {
return Promise.reject(error.response);
});
Thank for the help

Vuex: Axios GET request returns undefined inside component

I am using Vuex/Axios to make GET requests to an API. When a component mounts, I am dispatching an action to the Vuex store and making the Axios GET request. In the Vuex action, the Axios GET request returns the response as expected but the response inside the component is returning undefined. What am I doing wrong?
axios/index.js
import axios from 'axios';
const API_URL = 'http://localhost:3000/api/v1/';
const plainAxiosInstance = axios.create({
baseURL: API_URL,
withCredentials: true,
headers: {
'Content-Type': 'application/json'
}
});
export { plainAxiosInstance };
Vuex module: store/modules/character.js. In this file response logs the correct response. The fetchCharacters event gets triggered in a component.
import { plainAxiosInstance } from '#/axios';
const characterModule = {
namespaced: true,
state: {
characters: []
},
mutations: {
SET_CHARACTERS(state, characters) {
state.characters = characters;
}
},
actions: {
async fetchCharacters({ commit }) {
await plainAxiosInstance
.get('/characters')
.then(response => {
let characters = response.data;
commit('SET_CHARACTERS', characters);
console.log(characters); <-- Logs the expected response data
return characters;
})
.catch(error => console.log('Failed to fetch characters', error));
}
},
getters: {}
};
export default characterModule;
I am then dispatching the Vuex action inside of a Vue component on mount:
<script>
import { mapState, mapActions } from 'vuex';
export default {
mounted() {
this.fetchCharacters()
.then(response => {
console.log('response', response);
// response is logging undefined here <----
})
.catch(error => {
console.log(error);
});
},
computed: mapState(['character']),
methods: mapActions('character', ['fetchCharacters'])
};
</script>
The console.log in modules/character.js logs the data as expected and then the response inside of the component logs undefined. I made sure to return the variable characters in the Vuex module. And I also made the Vuex action fetchCharacters async. So why is the response in the component returning undefined?
Thanks if you can help.
Change this:
async fetchCharacters({ commit }) {
await plainAxiosInstance
to this:
fetchCharacters({ commit }) {
return plainAxiosInstance
You can keep the async if you want but it won't make any difference.
In its present form the action will implicitly return a promise and that promise won't resolve until the request is complete. However there's nothing to tell it to resolve that promise to the desired value.
Instead of waiting for the promise inside the action you can just return that promise instead. Externally that won't make any difference as you'll just get back a promise either way but crucially that promise will resolve to the correct value once the request is complete.

How to get data from axios call in redux action? React JS search project

I have a search of weather for some cities. I would like to create info modal when a user tries to find a city that is not in the base. In this case I receive 404 error from my API.
I fetch the data every time when user click on search button. I use axios to do it and whole project is based on React and Redux. Everything is clear for me but I have a problem with pass valid response to payload.
How should I do it? In an another file and use react component lifecycle?
action.js
export function fetchWeather(city) {
const url = `${ROOT_URL}&q=${city}`;
axios.get(url)
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
return {
type: FETCH_WEATHER,
payload: request
};
}
In your example the return will be called before Axios completes it's API call, because it's asynchronous. One solution to this is to put the return inside the .then like this:
export function fetchWeather(city) {
const url = `${ROOT_URL}&q=${city}`;
axios.get(url)
.then(function (response) {
// won't get called until the API call completes
console.log(response);
return {
type: FETCH_WEATHER,
payload: response.data
};
})
.catch(function (error) {
// won't get called until the API call fails
console.log(error);
return {
type: FETCH_WEATHER_ERROR,
payload: error
};
});
}
You should also return an error in the catch if the API call is unsuccessful.
In your snippet, request will always be undefined because axios.get is an async operation and return happens before axios.get finishes execution. You do something like this:
export async function fetchWeather(city) {
try {
const request = await axios.get(`${ROOT_URL}&q=${city}`);
// server returned a 2XX response (success)
return {
type: FETCH_WEATHER,
payload: request
};
} catch(error) {
// Network failure or 4XX or 5XX response.
return {
type: FETCH_WEATHER_FAIL
payload: error
}
}
}

React + Flux, ES6, Babel ActionCreate using json-server and super agent, data not in response

Hello I am attempting to use json-server to mock up the api of an React Flux ES6 app I am building. But when I user the superagent node module to make the request from the action creator the data in the callback is undefined
Here's my code
import Dispatcher from '../Dispatcher';
import Constants from '../Constants';
import request from 'superagent';
export default {
setQuestions(guides) {
Dispatcher.handleViewAction({
type: Constants.ActionTypes.SET_QUESTIONS,
data: guides
});
},
getQuestionsFromServer() {
let self = this;
let destination = 'http://localhost:3000/questionnaires';
// request from json service.
request
.get(destination)
.set({
'X-Requested-With': 'XMLHttpRequest'
})
.end(function(response) {
// response is empty. why???
if (response.ok) {
let guideData;
guideData = response.body;
self.setQuestions(guideData);
}
});
}
};
My network tab says the request happens but I cannot access the response in the callback.
I figured out how to make this xhr request without superagent node module by using the fetch es2015. See here:
https://developer.mozilla.org/en-US/docs/Web/API/GlobalFetch/fetch
getQuestionsFromServer() {
let self = this;
let destination = 'http://localhost:3000/questionnaires';
// request from json service.response.json()
fetch(destination)
.then(response => response.json())
.then(data => {
this.setQuestions(data[0].questions);
})
.catch(e => console.log("Error", e));
}
Thanks!

Categories