Weird React problem with axios and useState - javascript

my problem is about nested axios request. The code is very simple, with first axios.get i am fetching first portion of data and then using the given respons I am trying to fetch data from second endpoint. (code below)
const getData = async () => {
let returnSecoundValue = {}
try {
const response = await axios.get("http://127.0.0.1:8000/firstEndpoint/", {
auth: {
username: "x",
password: "y",
},
});
// console.log(response.data) <--- WORKS;
response.data.forEach(async element => {
const secoundData = await axios.get(`http://127.0.0.1:8000/secoundEndpoint/${element.id}/`, {
auth: {
username: "x",
password: "y",
},
});
returnSecoundValue[secoundData.data.id] = secoundData.data
});
console.log(returnSecoundValue) <--- WORKS;
setMyState({firstEnd: response.data, secoundEnd: empoRet})
} catch (err) {
console.error(err);
}
As in example above when I am finished fetching second data, I save it in my state as an object. The First element of an object is an array of objects, and the second is the object (used as disc).
Up to this point everything is fine, when I try and console.log it, it shows perfectly as I wanted. Problem starts when I'am trying to get the exact data from the second element.
With my fetched data I'am trying to do something like that:
myState.firstEnd.map((element) => {
try{
console.log(myState); (Works - log all values)
console.log(myState.secoundEnd); (Works - log all values of secoundEnd)
console.log(myState.secoundEnd[2]); (Return undefind)
<Component
key={element.id}
isActive={element.active}
title={element.title}
I have tried to do that in few approaches but each time I ended up with the same result. Is there a problem with mounting or is it something else?

Thanks to the comments, I figured out the problem.
This is the solution:
const secoundValue = response.data.map(async element => {
await axios.get(`http://127.0.0.1:8000/secoundEndpoint/${element.id}/`,
{
auth: {
username: "x",
password: "y",
},
});
});
Promise.all(secoundValue).then((res) => {
res.map((e)=>{
secoundEnd[e.data.id] = e.data
})
setMyState({ ads: resp.data, emp: empoRet });
});

Related

How to migrate request-promise to axios or fetch

I want to code a app with React-Native which loads JSON-files from a website with cookie-authentication.
For testing I tried it in a normal JS-file without React-native and with request-promise.
const fs = require("fs");
const request = require("request-promise").defaults({ jar: true });
async function main() {
var incodeHeader = "";
var incodeToken = "";
try {
const loginResult = await request.post("https://somepage/login.php", {
form: {
client: "XXX",
login: "username",
password: "password",
},
});
} catch (err) {
console.log(err);
}
incodeHeader = getIncodeHeader();
incodeToken = getIncodeToken();
const data = await request.post("https://somepage/load.json", {
headers: {
[incodeHeader]: incodeToken,
},
form: {
max: "10",
},
});
fs.writeFileSync("data.json", data);
}
main();
This worked well, so I wanted to use this method in my App, but I couldn't find a way to use request-promise in React-Native so I decided to use axios.
const axios = require("axios");
const qs = require("qs");
axios.defaults.withCredentials = true;
async function main() {
const data = {
client: "XXX",
login: "username",
password: "password",
};
await axios
.post("https://somepage/login.php", qs.stringify(data))
.catch((err) => console.log(err));
const incodeHeader = getIncodeHeader();
const incodeToken = getIncodetoken();
await axios
.get(
"https://somepage/load.json",
{ data: { max: "5" } },
{
headers: {
[incodeHeader]: incodeToken,
},
}
)
.then((respone) => console.log(respone))
.catch((err) => console.log(err));
}
main();
But in this code not even the login works and I really don't know why. Can somebody tell me how to do this right, or can tell me another solution which works in React-Native?
First, I don't know why you're stringifying the request body in the first request, axios already handle this, you can pass just the data object, maybe it's the solution for your problem.
Second (just a tip). Create a helper object to make http requests and do not instance axios directly, so then, you can change the http request handler in an easy way instead changing it on each file, one day you probably will need to do this if you want to keep your app updated.
Third, don't mix await and then, choose:
try {
const result = await action();
// ...
} catch (err) {
// ...
}
or
action()
.then((result) => {
// ...
})
.catch((err) => {
// ...
});
change await axios.get to await axios.post

Data Not updated when fetch query - React-Query?

I have a three-check box type,
When I check any box I call refetch() in useEffect().
The first time, I check all boxes and that returns the expected data!
but for some cases "rechange the checkboxes randomly", the returned data from API is "undefined" although it returns the expected data in Postman!
So I Guess should I need to provide a unique queryKey for every data that I want to fetch
so I provide a random value "Date.now()" but still return undefined
Code snippet
type bodyQuery = {
product_id: number;
values: {};
};
const [fetch, setFetch] = useState<number>();
const [bodyQuery, setBodyQuery] = useState<bodyQuery>({
product_id: item.id,
values: {},
});
const {
data: updatedPrice,
status,
isFetching: loadingPrice,
refetch,
} = useQuery(
['getUpdatedPrice', fetch, bodyQuery],
() => getOptionsPrice(bodyQuery),
{
enabled: false,
},
);
console.log('#bodyQuery: ', bodyQuery);
console.log('#status: ', status);
console.log('#updatedPrice: ', updatedPrice);
useEffect(() => {
if (Object.keys(bodyQuery.values).length > 0) {
refetch();
}
}, [bodyQuery, refetch]);
export const getOptionsPrice = async (body: object) => {
try {
let response = await API.post('/filter/product/price', body);
return response.data?.detail?.price;
} catch (error) {
throw new Error(error);
}
};
So after some elaboration in the chat, this problem can be solved by leveraging the useQuery key array.
Since it behaves like the dependency array in the useEffect for example, everything that defines the resulted data should be inserted into it. Instead of triggering refetch to update the data.
Here the key could look like this: ['getUpdatedPrice', item.id, ...Object.keys(bodyQuery.values)], which will trigger a new fetch if those values change and on initial render.

findOneAndUpdate mongoDB not returning properly

I am trying to push a user's choice as a string to their array of choices and return the updated document.
The route and function work successfully however it returns the User with an empty choice array. I believe the problem lies somewhere in the controller function but I cannot figure it out.
Any help is greatly appreciated!
To help, here is a screenshot of my console where you can see an empty choice array being returned.
Here is an image of my console.log
This is where I call the function
handleAnswerInput = (question) => {
let answerTextSelected = question.Text;
let answerTypeSelected = question.Type;
let usersName = this.state.user._id
this.setState({
count: this.state.count + 1
})
saveUserandScore(usersName, answerTextSelected)
.then(
this.loadQuestion(this.state.count)
)
console.log(answerTextSelected)
console.log(answerTypeSelected)
};
This is the controller function (updated from suggestions)
const saveUserAndTheirScore = (req, res) => {
let filter = { _id: req.params.id }
// let update = { choices: req.params.answer] }
console.log(req.params.id)
console.log(req.params.answer)
User.update(
{ filter },
{
$push: { choices: req.params.answer }
},
{
returnOriginal: false,
},
)
.then(dbData => res.json(dbData))
.catch(err => {
console.log(err);
res.json(err);
});
};
here is the axios call
export const saveUserandScore = (id, answer) => {
return axios.post(`/api/user/${id}/${answer}`);
};
you need to change user schema, in that you might have defined choices type as string. It must be an array.
findOneAndUpdate(filter, update, options, callback) has a returnOriginal option, if set to true (which is the default), it will return the document BEFORE the update. In your case, you might want to set it to false [1].
Unfortunately, the respective option for mongoose is named new [2].
[1] https://mongodb.github.io/node-mongodb-native/3.4/api/Collection.html#findOneAndUpdate
[2] https://mongoosejs.com/docs/api.html#query_Query-findOneAndUpdate

Issues with scope in try/catch while using async/await

My issue is that (seemingly) things are going out of scope, or the scope is being polluted when I enter my catch block in the function below:
export const getOne = model => async (req, res, next) => {
let id = req.params.id
let userId = req.user
try {
let item = await model.findOne({ _id: id, createdBy: userId }).exec()
if (!item) {
throw new Error('Item not found!')
} else {
res.status(200).json({ data: item }) // works perfectly
}
} catch (e) {
res.status(400).json({ error: e }) // TypeError: res.status(...).json is not a function
// also TypeError: next is not a function
// next(e)
}
}
Interestingly enough, using res.status(...).end() in the catch block works just fine, but it bothers me that I am not able to send any detail back with the response. According to the Express Documentation for res.send() and res.json I should be able to chain off of .status(), which, also interestingly enough, works just fine in the try statement above if things are successful - res.status(200).json(...) works perfectly.
Also, I tried abstracting the error handling to middleware, as suggested on the Express documentation, and through closures, I should still have access to next in the catch statement, right? Why is that coming back as not a function?
Why does res.status(...).json(...) work in my try but not catch block?
Why is next no longer a function in the catch block?
Thanks in advance!
Edit
This is failing in unit tests, the following code produces the errors described above:
describe('getOne', async () => {
// this test passes
test('finds by authenticated user and id', async () => {
expect.assertions(2)
const user = mongoose.Types.ObjectId()
const list = await List.create({ name: 'list', createdBy: user })
const req = {
params: {
id: list._id
},
user: {
_id: user
}
}
const res = {
status(status) {
expect(status).toBe(200)
return this
},
json(result) {
expect(result.data._id.toString()).toBe(list._id.toString())
}
}
await getOne(List)(req, res)
})
// this test fails
test('400 if no doc was found', async () => {
expect.assertions(2)
const user = mongoose.Types.ObjectId()
const req = {
params: {
id: mongoose.Types.ObjectId()
},
user: {
_id: user
}
}
const res = {
status(status) {
expect(status).toBe(400)
return this
},
end() {
expect(true).toBe(true)
}
}
await getOne(List)(req, res)
})
})
Why does res.status(...).json(...) work in my try but not catch block?
Seems like you're passing a non-express object that only has status & end methods when running using the unit testing. That's why it fails to find the json method

push array in const inside .then

I'm trying to push a value inside a const but its in a .then and it's not working do you know how can I do that ?
I get a value in my console.log(newResult) in my if but the data is not pushed in my const newResult in the return
res.status(200).json(newResult);
.then(function (friends) {
if (friends) {
const newResult = [];
friends.forEach((r) => {
if (r.UserID == userFound.id) {
models.User.findOne({
where: {
id: r.idFriend
}
})
.then(function(userFound) {
newResult.push({
id: r.id,
user: {
id: r.User.id,
email: userFound.email,
username: userFound.username
}
});
console.log(newResult)
})
} else
newResult.push({
id: r.id,
user: {
id: r.User.id,
email: r.User.email,
username: r.User.username
}
});
console.log(newResult)
});
res.status(200).json(newResult);
}
}
every test realised return an empty tab when i go in my if condition
It will never work because, you are doing async calls
models.User.findOne inside forEach.
You'll get results on console.log when async call to database for fetching user is complete.
But before this all happens the forEach is done executing and code hits the line res.status(200).json(newResult); and you see no results from your if condition.
Instead of using this approach go for mongoose populate and populate userObject based userID while finding friends this way you won't have to do async call inside the forEach.
Read about mongoose populate at: http://mongoosejs.com/docs/populate.html

Categories

Resources