"Objects are not valid as a React child" error - javascript

the data is not displayed by REACT and the following error is received: "Objects are not valid as a React child. If you meant to render a collection of children, use an array instead"
The records from MongoDB collection are fetched and gathered in an array of objects. then I use the .map() function to produce the array of elemens to be rendered by the Display component. Each element includes the component which receives two props (firstName and age)
i do not see where is my mistake...
thanx for help!
SingleRecord.js:
const SingleRecord = (firstName, age) => {
return (
<li className="singe-record">
{firstName} is {age} years old.
</li>
);
}
export default SingleRecord;
Display.js:
function Display() {
const [records, setRecords] = useState();
const dataArray = [];
const fetchRecords = () => {
fetch('http://localhost:3001/users')
.then(async response => {
const isJson = await response.headers.get('content-type')?.includes('application/json');
const data = isJson ? await response.json() : null;
for (const elem of data) {
let elemObj = {
_id: elem._id,
firstName: elem.firstName,
age: elem.age};
dataArray.push(elemObj);
}
setRecords(dataArray);
// check for error response
if (!response.ok) {
// get error message from body or default to response status
const error = (data && data.message) || response.status;
return Promise.reject(error);
}
})
.catch(error => {
console.error('There was an error!', error);
});
}
useEffect(() => {
fetchRecords();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
if (!records) {
return null;
}
const LI = records.map(elem => {
let fn = elem.firstName;
let ageee = elem.age;
return <li><SingleRecord firstName={fn} age={ageee} /></li>
})
return (
<div className="records-display">
<h2>Records:</h2>
<ul className ="records-list">
{LI}
</ul>
</div>
);
}
app.js (backend):
const { MongoClient } = require("mongodb");
const uri = "...hidden...";
const client = new MongoClient(uri);
const database = client.db('holdocsDB');
const records = database.collection('records');
app.get('/users', async (req, res) => {
const cursor = await records.find();
const results = await cursor.toArray();
res.send(results);
})
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.json('error');
});

Its because LI is an array, so you would get the same error if you did something like:
{['foo', 'bar']}
change it to this and give it a try:
return (
<div className="records-display">
<h2>Records:</h2>
<ul className ="records-list">
{records.map(elem => {
let fn = elem.firstName;
let ageee = elem.age;
return <li><SingleRecord firstName={fn} age={ageee} /></li>
})}
</ul>
</div>
);

Related

http://localhost:9000/api/users net::ERR_CONNECTION_REFUSED and jwt must be provided

I am creating a blog application. In every parameterized route I get the details of that specific user.For eg: /profile/#randomuser get the details of #randomusers, /profile/#robert get the details of #robert. I get the details of parameterized toute users but not the user who log in to the blog application.
Everything is imported correctly.
<Route path="/profile/:profile" element={\<Profile /\>}
Profile.jsx
const [user, getUser] = useState([])
const [glbUser, setUser] = useContext(UserContext)
const match = useParams().profile
const match = useMatch('/profile/:profile')
const userMatch = match.params.profile
console.log(" usematchis ", userMatch)
const userParams = useParams().profile
useEffect(() => {
async function fetchGlbUser() {
const loggedInUser = window.localStorage.getItem('userToken')
if (loggedInUser) {
const user = JSON.parse(loggedInUser)
loginServices.setToken(user.token)
// loginServices.createToken()
const config = { headers: { Authorization: user.token } }
const glbUser = await axios.get("${postUrl}", config)
setUser(glbUser.data.glbUserToken)
return glbUser.data.glbUserToken
}
}
fetchGlbUser()
}, [])
// console.log("Match is", match)
// console.log("Type of Match is", typeof match.params.profile)
useEffect(() =\> {
axios.get("http://localhost:9000/api/users/${userMatch}", { params: { profile: userMatch } })
.then(res =\> {
console.log(res.data)
getUser(res.data)
// getUser(res.data.newUser)
})
.catch(err => console.log(err))
// const getUserData = async () => {
// const res = loginServices.getProfile(`http://localhost:9000/api/users/${userMatch}`, {params:{profile:userMatch}})
// return res.data
// }
// getUserData()
}, [])
Backend.js
router.get('/:profile', async (req, res) =\> {
console.log(req)
const username = req.params.profile
const decodedToken = jwt.verify(getToken(req), process.env.SECRET_KEY)
// console.log(username)
// console.log(typeof username)
try {
const newUser = await User.findOne({ username })
const glbUserToken = await User.findById(decodedToken.id)
// console.log(newUser)
res.status(200).json({ newUser, glbUserToken })
} catch (err) {
console.log(err)
}
})
const getToken = req =>
const auth = req.get('authorization')
if (auth && auth.startsWith(`bearer `))
return auth.replace('bearer ', '') }
return null
}

I want to use for loop in async function with "thens" in javascript

I'm creating a simple web app that fetches data asynchronously from three web apis. One is for location, one for weather and one for stock images. My files are as follow:
Index.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Weather Journal</title>
</head>
<body>
<div id="app">
<div class="holder headline">
Weather Journal App
</div>
<form id="userInfo">
<div class="holder zip">
<label for="city">Enter City here</label>
<input type="text" id="city" placeholder="enter city here" required>
</div>
<div class="holder feel">
<label for="date">Enter departure date</label>
<input type="datetime-local" id="date" required>
<button id="submitBtn" type="submit"> Generate </button>
</div>
</form>
<div class="holder entry">
<div class="title">Details</div>
<div>
<img id="city-pic" src="" alt="your city">
</div>
<div id="entryHolder">
<div><b>Temperature for next 16 days:</b></div>
<ul id="entries">
<div id="temp"></div>
<div id="time"></div>
</ul>
</div>
</div>
</div>
<script src="app.js" type="text/javascript"></script>
</body>
</html>
app.js:
const present = new Date();
const submitBtn = document.getElementById("submitBtn");
submitBtn.addEventListener("click", (e) => {
e.preventDefault();
const city = document.getElementById("city").value;
const departure = document.getElementById("date").value;
const [depart_date, depart_time] = departure.split("T")
const [depart_year, depart_month, depart_day] = depart_date.split("-")
const [depart_hour, depart_minute] = depart_time.split(":")
const future = new Date(depart_year, depart_month - 1, depart_day, depart_hour, depart_minute);
if (city !== "" || departTime !== "" || future < present) {
document.getElementById("time").innerHTML = `Departure in ${Math.ceil((future - present) / 3600000 / 24)} days`
getCity(geoURL, city, geoUsername)
.then(function (data) {
return getWeather(weatherURL, weatherKey, data["geonames"][0]['lat'], data["geonames"][0]['lng'])
}).then(weatherData => {
return postWeatherData("/addWeather", { temp: weatherData['data'][i]['temp'], datetime: weatherData['data'][i]['datetime'] })
}).then(function () {
return receiveWeatherData()
}).catch(function (error) {
console.log(error);
alert("Please enter a valid city and a valid time");
})
getPictures(city, pixabayURL, pixabayKey)
.then(function (picsData) {
const total = picsData['hits'].length
const picIndex = Math.floor(Math.random() * total)
return postPictureData("/addPicture", { pic: picsData['hits'][picIndex]["webformatURL"] })
})
.then(function () {
return receivePictureData()
}).catch(function (error) {
console.log(error);
alert("No pictures found")
})
}
})
const getCity = async (geoURL, city, geoUsername) => {
const res = await fetch(`${geoURL}q=${city}&username=${geoUsername}`);
try {
const cityData = await res.json();
return cityData;
}
catch (error) {
console.log("error", error);
}
}
const postWeatherData = async (url = "", data = {}) => {
const response = await fetch(url, {
method: "POST",
credentials: "same-origin",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
temp: data.temp,
datetime: data.datetime
})
});
try {
const newData = await response.json();
console.log(newData)
return newData;
}
catch (error) {
console.log(error);
}
}
const receiveWeatherData = async () => {
const request = await fetch("/allWeather");
try {
const allData = await request.json()
const node = document.createElement("li");
const textnode = document.createTextNode("DATE: " + allData['datetime'] + "\t" + "TEMPERATURE: " + allData['temp']);
node.appendChild(textnode);
document.getElementById("entries").appendChild(node);
}
catch (error) {
console.log("error", error)
}
}
const getWeather = async (weatherURL, weatherKey, lat, lon) => {
const res = await fetch(`${weatherURL}&lat=${lat}&lon=${lon}&key=${weatherKey}`);
try {
const weatherData = await res.json();
return weatherData;
}
catch (error) {
console.log("error", error);
}
}
const getPictures = async (city, pixabayURL, pixabayKey) => {
const query = city.split(" ").join("+");
const res = await fetch(`${pixabayURL}key=${pixabayKey}&q=${query}`);
try {
const picsData = await res.json();
return picsData;
}
catch (error) {
console.log("error", error)
}
}
const receivePictureData = async () => {
const request = await fetch("/allPictures");
try {
const allData = await request.json()
document.getElementById("city-pic").src = allData['pic'];
}
catch (error) {
console.log("error", error)
}
}
const postPictureData = async (url = "", data = {}) => {
const response = await fetch(url, {
method: "POST",
credentials: "same-origin",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
pic: data.pic
})
});
try {
const newData = await response.json();
return newData;
}
catch (error) {
console.log(error);
}
}
server.js:
// Setup empty JS object to act as endpoint for all routes
cityData = {};
weatherData = {};
picturesData = {};
// Require Express to run server and routes
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
// Start up an instance of app
const app = express();
/* Middleware*/
//Here we are configuring express to use body-parser as middle-ware.
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
// Cors for cross origin allowance
app.use(cors())
// Initialize the main project folder
app.use(express.static('dist'));
app.use(express.static('website'));
app.get("/all", function sendData(req, res) {
res.send(cityData);
})
app.get("/allWeather", function sendWeather(req, res) {
res.send(weatherData);
})
app.get("/allPictures", function sendPictures(req, res) {
res.send(picturesData);
})
app.post("/addWeather", (req, res) => {
weatherData['temp'] = req.body.temp;
weatherData['datetime'] = req.body.datetime;
res.send(weatherData);
})
app.post("/addPicture", (req, res) => {
picturesData['pic'] = req.body.pic;
res.send(picturesData);
})
// Setup Server
app.listen(3000, () => {
console.log("App listening on port 3000")
console.log("Go to http://localhost:3000")
})
The server uses node, express, cors, and body-parser as the tools to create it.
I have not included the api keys or usernames. Right now I need to loop over the weather data that is return from the api (which fetches 16 days of data). The code :
.then(weatherData => {
return postWeatherData("/addWeather", { temp: weatherData['data'][i]['temp'], datetime: weatherData['data'][i]['datetime'] })
should use the 'i' variable to loop over all the possible 16 entries for a certain location. Right now if run the app with 0 in place of 'i' it just gives the temperature for the next day. I want to get the weather data for 16 days and append it to the html 'ul' that I have in the document. Can someone guide me. This is the last step in the project that I need to complete by 10 november!
EDIT:
I tried using this and subsituted forLoop() instead the 'thens' but i get an error:
const forLoop = async () => {
for (i = 0; i < 16; i++) {
try {
coords = await getCity(geoURL, city, geoUsername)
weatherData = await getWeather(weatherURL, weatherKey, coords["geonames"][0]['lat'], coords["geonames"][0]['lng'])
await postWeatherData("/addWeather", { temp: weatherData['data'][i]['temp'], datetime: weatherData['data'][i]['datetime'] })
await receiveWeatherData(i);
}
catch (error) {
console.log(error);
// alert("Please enter a valid city and a valid time");
}
}
}
The error is: "16app.js:156 TypeError: Cannot read properties of undefined (reading 'lat')
at forLoop (app.js:150:89)"
A series of promises can be created using Array.map() over received weather data, and a series of promises can be resolved using Promise.all(). The returned output would be an array of received temperature information. Please see the below code
(PromiseLikeObject)
.then(weatherData => {
const promiseCollection = weatherData['data'].map(d => postWeatherData("/addWeather", { temp: d.temp, datetime: d.datetime }));
return Promise.all(promiseCollection);
});
If I understood you right, I think you could just map over the data and display it in DOM, just adjust the to the array you receive.
const postWeatherData = async (url = "", data = {}) => {
const response = await fetch(url, {
method: "POST",
credentials: "same-origin",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
temp: data.temp,
datetime: data.datetime
})
});
try {
const newData = await response.json();
let entryHolder = document.getElementById("entryHolder");
const weather = newData.map((data, index) => {
return `<ul id="entries">
<div id="temp">${data.temp}</div>
<div id="time">${data.time}</div>
</ul>`
}).join('')
entryHolder.innerHTML = weather
}
catch (error) {
console.log(error);
}
}
I have fixed it with for loop:
const forLoop = async () => {
for (i = 0; i < 16; i++) {
try {
const city = await document.getElementById("city").value;
const coords = await getCity(geoURL, city, geoUsername)
const weatherData = await getWeather(weatherURL, weatherKey, coords["geonames"][0]['lat'], coords["geonames"][0]['lng'])
postWeatherData("/addWeather", { temp: weatherData['data'][i]['temp'], datetime: weatherData['data'][i]['datetime'] })
receiveWeatherData(i)
}
catch (error) {
console.log(error);
// alert("Please enter a valid city and a valid time");
}
}
}

code works fine but the console print cannot set headers after they are sent to the client

guys i create a function to limit the user`s upload images everything works fine but the problem that
i got an Error on the console (Cannot set headers after they are sent to the client)
and when i copy the whole code of the function note(! not the function itself ) . on the controllers file i didnt get the error however ( its the same code and same everything
but the code is too long so it looks bad
please check the image before looking into the code
const Joi = require('joi');
const appError = require('./appError')
module.exports.validateHadith = (req,res,next)=>{
const hadithSchema = Joi.object({
Hadith: Joi.object({
narrator: Joi.string().required(),
description:Joi.string().required().min(15),
hadith:Joi.string().required().min(15),
}).required()
})
const {error} = hadithSchema.validate(req.body);
if(error){
const msg = error.details.map(el=>el.message).join(',')
throw new appError(msg,400)
} else{
next()
}
};
module.exports.reviewValidation = (req,res,next) => {
const reviewJoiSchema = Joi.object({
review: Joi.object({
comment: Joi.string().required(),
rating: Joi.number().required(),
}).required()
})
const {error} = reviewJoiSchema.validate(req.body);
if(error) {
const msg = error.details.map(el=>el.message).join(',')
throw new appError(msg,400)
} else {
next()
}
}
module.exports.imageValidation = (req,res,next) => {
const imageSchema = Joi.array().min(1).max(6).required()
const {error} = imageSchema.validate(req.files)
if(error) {
const msg = error.details.map(el=>el.message).join(',')
throw new appError(msg,400)
} else{
next()
}
}
module.exports.imageEditValidation = async (updatedHadith,images,req,res,id) => {
const length = updatedHadith.images.length
const max = 6
if(images.length+length <= max) {
updatedHadith.images.push(...images)
await updatedHadith.save()
console.log(updatedHadith)
} else{
req.flash('error','Sorry you can`t have more than 6 images')
return res.redirect(`/hadith/${id}`)
}
}
You need to add await
await imageEditValidation(updateHadith, images,req, res, id)
Currently, it's not awaiting, so it's jumping to req.flash("success", "successfully editing the Hadith") without waiting for it to be returned.
You also need to add a return after `console.log(updatedHadith)
Also need to remove req.flash and res.redirect('/hadith/${id}') statement, and replace it with a return
the validator code will be
module.exports.imageEditValidation = async (updatedHadith,images,req,) => {
const length = updatedHadith.images.length
const max = 6
if(images.length+length <= max) {
updatedHadith.images.push(...images)
await updatedHadith.save()
console.log(updatedHadith)
return true
} else{
req.flash('error','Sorry you can`t have more than 6 images')
return false
}
}
and controller code will be
module.exports.postEditForm =async (req, res, next) => {
const {id} = req.params;
const updatedHadith = await HadithModel.findByIdAndUpdate(
id,
{...req.body.Hadith},
{ runValidators: true }
);
const images = req.files.map(f => ({url : f.path ,filename: f.filename}))
if(await imageEditValidation (updatedHadith,images,req)){
req.flash("success", " The Hadith is successfully Edited :)");
}
res.redirect(`/hadith/${id}`);
}

create middlware with input argument in nodejs

i want to create a misslware in nodejs for access Level , i create this middlware :
class AccessUser extends middlware {
async AccessUser(access,req, res, next) {
const getTokenFrom = (req) => {
const authorization = req.headers["authorization"];
if (authorization && authorization.toLowerCase().startsWith("bearer ")) {
return authorization.substring(7);
}
return null;
};
const token = getTokenFrom(req);
if (token) {
jwt.verify(token, "shhhhh", (err, decoded) => {
if (err) return new ForbiddenResponse().Send(res);
let permission = decoded.info.permission;
let item = permission.find((x) => x.permissionId == access);
if (!item) {
return new ForbiddenResponse().Send(res);
} else {
next();
}
});
}
}
}
i add the argument name access to input of AccessUser in this middlware :
async AccessUser(access,req, res, next)
and i want to need compare the access with x.permissionId . but it show me this error :
(node:2168) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'authorization' of undefined
this error for this line :
const authorization = req.headers["authorization"];
and i use this middlware by this :
router.post(
"/Create",
access.AccessUser("Role:Add")
);
now whats the problem ? how can i use the middllware with input argument ?????
AccessUser is not a express middleware, the method signature has to be (req,res,next).
You can get around this if you define AccessUser as a factory function (there's no need to define it async as you're not awaiting any async call):
class AccessUser {
accessUser(access) {
return function (req, res, next) {
const getTokenFrom = (req) => {
const authorization = req.headers["authorization"];
if (authorization && authorization.toLowerCase().startsWith("bearer ")) {
return authorization.substring(7);
}
return null;
};
const token = getTokenFrom(req);
if (token) {
jwt.verify(token, "shhhhh", (err, decoded) => {
if (err) return new ForbiddenResponse().Send(res);
let permission = decoded.info.permission;
let item = permission.find((x) => x.permissionId == access);
if (!item) {
return new ForbiddenResponse().Send(res);
} else {
next();
}
});
}// TODO: handle case if no token exists?
}
}
}
module.exports = AccessUser;
Then use it like this:
const AccessUserMiddleware = require('./path-to-access-middleware');
const AccessUser = new AccessUserMiddleware();
app.get('/', AccessUser.accessUser("Role:Add"));

How to structure my data in Firebase using NodeJS

How I want my data to be structured is as follows:
Student -> Reg_num -> someindex (that will start from 1 - like an
unique key) -> course details.
However, the code I wrote gives me an incorrect structure. Can someone help me sort it out.
var db = admin.database();
var ref = db.ref("Students");
var newMessageRef = ref.push();
exports.uploadFile = functions.https.onRequest((req, res) => {
cors(req, res, () => {
var uniqueID = 97888888888888;
if (req.method !== 'POST') {
return res.status(500).json({
message: 'Not allowed'
})
} else {
return newMessageRef.set({
[uniqueID]: {
course: req.body.course,
credits: req.body.credit,
lecturer : 'Prof. Lee'
}
}).then(() => {
res.status(200).json({
message: "okkkkasss"
});
...
Note: The -LC-lS2HPMbZW9AdT19K is a code that was automatically added from the code. This is because I used ref.push()
Do not use ref.push() or ref.set(), but ref.update() as follows:
const db = admin.database();
const ref = db.ref("Students");
//var newMessageRef = ref.push(); <- Don't do that
exports.uploadFile = functions.https.onRequest((req, res) => {
cors(req, res, () => {
const uniqueID = 97888888888888; <- Student ID
const uniqueCourseKey = 0; <- uniqueCourseKey
if (req.method !== 'POST') {
return res.status(500).json({
message: 'Not allowed'
})
} else {
return ref.child(uniqueID).update({
[uniqueCourseKey]: {
course: req.body.course,
credits: req.body.credit,
lecturer : 'Prof. Lee'
}
}).then(() => {
res.status(200).json({
message: "okkkkasss"
});
....
Then you can call again the Cloud Function with e.g. const uniqueCourseKey = 1; and the new node will be correctly added under the StudentID node.
The doc for the update method is here: https://firebase.google.com/docs/reference/js/firebase.database.Reference#update

Categories

Resources