Copy nested objects from Axios response to my React-native hook? - mongodb

Im simply trying to copy the Nested objects i get back from the axios GET request to my react-native hook. Not straightforward it seems. Data would look something like this for example:
[
{
_id: 61242b08013a5f26bd1b2d47,
user: '6110675d65e1528d03a8bce6',
totalCalories: 7,
totalProtein: 7,
createdAt: 2021-08-23T23:11:04.076Z,
updatedAt: 2021-08-24T00:53:38.621Z,
__v: 0
},
{
_id: 6125990e9669cc6b466c37b5,
user: '6110675d65e1528d03a8bce6',
__v: 0,
createdAt: 2021-08-25T01:12:44.343Z,
totalCalories: 2,
totalProtein: 2,
updatedAt: 2021-08-25T01:14:01.439Z
}
]
However, i get a component exception: undefined is not an object error, as well as a 404 error when trying to access it via historyData in my frontend. Here is my component which renders the history screen in my iOS app:
Frontend:
const History = () => {
const [currentUsersID, setCurrentUsersID] = React.useState("");
const [historyData, setHistoryData] = React.useState();
// Gets the current user's ID from local storage
const getData = async () => {
try {
const value = await AsyncStorage.getItem("#storage_Key");
if (value !== null) {
setCurrentUsersID(value);
}
} catch (error) {
console.log("Error reading AsyncStorage value -> " + error);
}
};
getData();
async function getHistory() {
try {
const response = await axios.get("http://localhost:5000/daysLog/getLog/" + currentUsersID);
setHistoryData(() => {
return response.data;
});
} catch (error) {
console.log("ERROR (getHistory) -> " + error);
}
}
useFocusEffect(
React.useCallback(() => {
getHistory();
})
);
return (
<SafeAreaView style={styles.container}>
<StatusBar barStyle="light-content" />
<Text style={{ color: "white" }}>
History: {historyData[0].totalCalories} // ERROR HERE
</Text>
</SafeAreaView>
);
};
Backend:
const router = require("express").Router();
let daysLog = require("../models/daysLog.model");
// getting the user's existing daysLogs
router.route("/getLog/:userID").get((req, res) => {
daysLog
.find({
user: req.params.userID,
})
.then((logs) => res.json(logs))
.catch((error) =>
res.status(400).json("Error (dayLog/GET) -> " + error)
);
});

historyData[0].totalCalories is throwing that because it will take time for it to get fetched while you're waiting for a response. You should have a block to test if historyData is not nul before you render the result.
Also get history focus effect relies on currentUser being valid but there's no expression that ensures it the way you wrote it. At best it's a race condition. Consider changing your focus effect to be a regulaf effect and make the currentUserId it's dependency.
Then inside of it to you can check if currentUserId is not null and start fetching get history accordingly.

Related

Mongoose Error stating that it cannot populate a path because its not in my schema - but im not populating anything?

I have two routes in my server. Both routes are identical minus one using $push and one using $pull. The $pull route works as it should but always responds with an error of
"MongooseError: Cannot populate path scenes because it is not in
your schema. Set the strictPopulate option to false to override."
while the $push route works perfect and responds accordingly. I am very confused as to why I am getting a populate error considering I am not populating? I do have a 'scenes' property but not in either Model being used in these routes.
router.put('/scene/:sceneId/addactor/:actorId', async (req, res) => {
const actorId = req.params.actorId;
const sceneId = req.params.sceneId;
try {
const actor = await Actor.findById(actorId);
await Scene.findByIdAndUpdate(sceneId, { $push: { actors: actor } }, { new: true }).then(dbSceneData => {
res.status(200).send(dbSceneData);
})
} catch (error) {
res.status(400).send(`An Error Ocurred: ${error}`);
}
});
router.put('/scene/:sceneId/removeactor/:actorId', async (req, res) => {
const actorId = req.params.actorId;
const sceneId = req.params.sceneId;
try {
const actor = await Actor.findById(actorId);
await Scene.findByIdAndUpdate(sceneId, { $pull: { actors: actor } }, { new: true }).then(dbSceneData => {
res.status(200).send(dbSceneData);
})
} catch (error) {
res.status(400).send(`An Error Ocurred: ${error}`);
}
});

How can i make the interceptor run a function on error exept for one specific request?

this is my interceptor:
axios.interceptors.response.use(
(response) => {
if (error.response?.status === 403) {
unstable_batchedUpdates(() => {
// to force react state changes outside of React components
useSnackBarStore.getState().show({
message: `${i18n.t('forbidden')}: ${error.toJSON().config.url}`,
severity: 'error',
})
})
}
return Promise.reject(error)
}
)
I want this behavior all the time except when I make this specific call or at least except every head call
export const companiesQueries = {
headCompany: {
name: 'headCompany',
fn: async (companyId) => {
return await axios.head(`/companies/${companyId}`)
},
},
fixed by applying these changes to the api call:
const uninterceptedAxiosInstance = axios.create()
headCompany: {
name: 'headCompany',
fn: async (companyId) => {
return await
uninterceptedAxiosInstance.head(`/companies/${companyId}`)
},
}

Failed to add new elements when set initialState as an empty object

I try to use redux toolkit and I have this as menu-slice.js
I try to use property accessors to add a new property to fileItems, its initial value is an empty object.
import { createSlice } from "#reduxjs/toolkit";
const menuSlice = createSlice({
name: "ui",
initialState: {
fileItems: {},
},
reducers: {
setFileDate: (state, action) => {
state.FileDate = action.payload;
},
replaceFileItems: (state, action) => {
const filesList = action.payload.map((fileName) =>
fileName.slice(fileName.indexOf("/") + 1)
);
state.fileItems[state.FileDate] = filesList;
console.log(`filesList: ${filesList}`);
console.log(`state.fileItems: ${JSON.stringify(state.fileItems)}`);
console.log(`state.FileDate: ${state.FileDate}`);
state.fileContents = null;
},
I call dispatch with the api return value ( dispatch(menuActions.replaceFileItems(fileResponse.data));)
in menu-action.js:
the return value is an array of strings.
export const fetchFiles = (fileDate) => {
return async (dispatch) => {
const fetchFilesList = async () => {
const response = await fetch(
"some url" +
new URLSearchParams({
env: "https://env.com",
date: fileDate,
})
);
if (!response.ok) {
throw new Error("Fail to fetch files list!");
}
const data = await response.json();
return data;
};
try {
const fileResponse = await fetchFilesList();
dispatch(menuActions.setFileDate(FileDate));
dispatch(menuActions.replaceFileItems(fileResponse.data));
} catch (error) {
dispatch(
menuActions.showNotification({....
})
);
}
};
};
But it never prints console logs and didn't display where went wrong in the console or in the chrome redux extension.
I want to add data into state.fileItems on each click that triggers fetchFiles() when it returns a new array:
from state.fileItems = {}
check if state.fileItems already has the date as key,
if not already has the date as key,
change to ex: state.fileItems = {"2022-01-01": Array(2)}
and so on..
ex: state.fileItems = { "2022-01-01": Array(2), "2022-01-02": Array(2) }
I also tried to set state.fileItems as an empty array, and use push, but it didn't work either, nothing printed out, state.fileItems value was always undefined.
Can anyone please tell me why this didn't work?
Thanks for your time to read my question.

Uncaught (in promise) TypeError: Cannot use 'in' operator to search for 'validateStatus' in

I am getting ** Uncaught (in promise) TypeError: Cannot use 'in' operator to search for 'validateStatus' in 5f8425a33a14f026f80133ed** where 5f8425a33a14f026f80133ed is the id passed to the axios url
I want to display the services based on the user id. My url works perfectly in postman but when i access it from the veux store it gives an error.
services.js (store)
import axios from 'axios';
const state = {
services : {},
status: '',
error: null
};
const getters = {
services : state => { return state.services }
};
const actions = {
async fetchServices({commit}, userId) {
let res = await axios.get('http://localhost:5000/api/services/displayUser' , userId)
commit('setProducts', res.data)
return res;
}
};
const mutations = {
setProducts (state, items) {
state.services= items
},
};
export default {
state,
actions,
mutations,
getters
};
This is how I am calling the action :
computed: {
...mapGetters(["services"]),
},
methods: {
...mapActions(["fetchServices"]),
getData(){
this.fetchServices(this.user._id)
},
},
async created() {
await this.getProfile();
await this.getData();
}
The axios route is defined as
router.get('/displayUser', (req,res) => {
const query = user = req.body ;
Services.find(query)
.exec((err, services) => res.json(services))
})
the error screenshot :
Error screenshot
GET request should not have a body. Either use query params, indicate an id in a path, or use POST request.
In case of query params this may look like this:
let res = await axios.get('http://localhost:5000/api/services/displayUser' , { params: { userId })
router.get('/displayUser', (req,res) => {
const query = user = req.query;
Services.find(query)
.exec((err, services) => res.json(services))
})
This worked for me too:
In front end: Vue Js
let res = axios.get("http://localhost:3000/api/v1/role/getRoleByName",
{ params: { roleName: "name of role you want to send as params" },
});
In back end: Node Js
router.get('/getRoleByName', (req,res)=>{
let roleName = req.query.roleName;
roleModule.getRoleByName(roleName).then(data =>{
response.json(res,data)
}
).catch(err=> {
response.badRequest(res, err);
})
});
it's a silly mistake axios.post req.
async addTodo({ commit }, title) {
try {
const res = await axios.post(BASE_URL, { title, complete: false });
commit("newTodo", res.data);
} catch (err) {
console.log(err.message);
}
},

Multiple functions in restify function to elasticsearch client

I'm building a REST API using node and restify that communicaties with an elasticsearch database. Now when I delete an object, I want this to do a kind of cascading delete to some other objects. I know this is not really what to use elasticsearch for but bear with me.
So here is my code:
function deleteHostname(req, res, next) {
res.setHeader('Access-Control-Allow-Origin', '*');
var endpoints = [];
client.search({
index: 'test',
type: 'something',
body: {
from: 0, size: 100,
query: {
match: {
hostname: 'www.test.com'
}
}
}
}).then(function (error, resp) {
if(error) {
res.send(error);
}
endpoints = resp.hits.hits;
for (index = 0, len = endpoints.length; index < len; ++index) {
client.delete({
index: 'test',
type: 'something',
id: endpoints[index]._id
}, function (error, response) {
if(error) {
res.send(error);
}
});
}
res.send(endpoints);
return next();
});
}
So basically I just want to search for any objects with hostname www.test.com ( I just hard coded this to test it ). Then I want to delete all objects I found. It follows the error path and sends me this:
{
"took":1,
"timed_out":false,
"_shards":{
"total":5,
"successful":5,
"failed":0
},
"hits":{
"total":1,
"max_score":2.098612,
"hits":[
{
"_index":"test",
"_type":"something",
"_id":"123456",
"_score":2.098612,
"_source":{
"duration":107182,
"date":"2016-05-04 00:54:43",
"isExceptional":true,
"hostname":"www.test.com",
"eta":613,
"hasWarnings":false,
"grade":"A+",
"ipAddress":"ipip",
"progress":100,
"delegation":2,
"statusMessage":"Ready"
}
}
]
}
}
So in my opinion this doesn't look like an error? So why am I getting it back as an error? If I remove:
if(error) {
res.send(error);
}
From my code, I won't get any response.
You need to change your code like this (see the changes denoted by -> to the left):
if(error) {
1-> return res.send(error);
}
endpoints = resp.hits.hits;
for (index = 0, len = endpoints.length; index < len; ++index) {
2-> (function(id){
client.delete({
index: 'test',
type: 'something',
3-> id: id
}, function (error, response) {
if(error) {
4-> next(error);
}
});
5-> })(endpoints[index._id]);
}
6-> //res.send(endpoints);
I'm now explaining each change:
If you don't return you'll send the error and then you'll continue with processing the hits
(3/5) Since client.delete is an asynchronous function, you need to call it in an anonymous function
In case of error you need to call next(error) not res.send
You cannot send the response at this point since your for loop might not be terminated yet. Instead of a for loop, you should use the excellent async library instead (see an example of using asynch.each below)
Async example:
var async = require('async');
...
if(error) {
return res.send(error);
}
endpoints = resp.hits.hits;
async.each(endpoints,
function(endpoint, callback) {
client.delete({
index: 'test',
type: 'something',
id: endpoint._id
}, callback);
},
// this is called when all deletes are done
function(err){
if (err) {
next(err);
} else {
res.send(endpoints);
next();
}
}
);
Another solution for you to achieve exactly what you want is to use the delete by query plugin. That feature allows you to do all the above in a single query.
If you are still on ES 1.x, delete-by-query is still part of the core and you can simply call the deleteByQuery function of the Javascript client.
If you are on ES 2.x, delete-by-query is now a plugin, so yo need to install it and then also require the deleteByQuery extension library for the Javascript client
function deleteHostname(req, res, next) {
res.setHeader('Access-Control-Allow-Origin', '*');
client.deleteByQuery({
index: 'test',
type: 'something',
body: {
query: {
match: { hostname: 'www.test.com' }
}
}
}, function (error, response) {
if (error) {
next(error);
} else {
res.send(endpoints);
next();
}
});
}