"Objects are not valid as a React child" react 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 elements to be rendered by the Display component. Each
element includes the component which receives two props (firstName and age)
i still 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">
{records.map(elem => {
let fn = elem.firstName;
let ageee = elem.age;
return <li><SingleRecord firstName={fn} age={ageee} /></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');
});

SingleRecord = (firstName, age) should be SingleRecord = ({ firstName, age })
Props aren't passed as individual arguments, they're all in the first argument as an object. Trying to render {firstName} is causing you to try to render the entire props object, and the react error React is telling you that's an issue.
And you aren't using the LI variable. And did you read the error? Get in the habit of always reading errors, don't ignore them.

Related

How do I return each collection and its documents in firestore with cloud functions?

Would like to return an array that has [1year, 1month, etc] and each of those are arrays that contain each document.
Currently, this returns an empty array but when I print the size of the snapshots I get the correct values. Not sure if i'm using push() correctly or if this is an async issue. Thanks.
exports.getStockPrices = functions.https.onRequest((req, res) => {
cors(req, res, () => {
const currentUser = {
token: req.headers.authorization.split('Bearer ')[1]
};
// ! this is a post request
admin
.auth()
.verifyIdToken(currentUser.token)
.then(decodedToken => {
// keep this just in case we want to add anything to do with the user
const user = decodedToken;
// array of collections e.g [1year, 1mo, etc]
const data = [];
// array of documents e.g [18948901, 1984010471, etc]
const documents = [];
db.collection('historical')
.doc(`${req.body.ticker}`)
.listCollections()
.then(collections => {
// each collection is the 1year, 1mo, etc
collections.forEach(collection => {
collection.get().then(querySnapshot => {
console.log('number of documents: ' + querySnapshot.size);
querySnapshot.forEach(doc => {
// doc.data is each piece of stock data
documents.push(doc.data());
});
// each document e.g 1year, 1mo, etc
data.push(documents);
});
});
return data;
})
.then(data => {
return res.json({ data });
})
.catch(err => {
console.log(err);
return res.status(500).send({ error: 'error in getting data' });
});
})
.catch(err => {
console.log(err);
return res.status(500).send({
error: 'error authenticating user, please try logging in again'
});
});
});
});
Due the nature of async calls, your return occurs before your array is being filled.
You can try my example, my firebase function is defined as async this allows me to use await, this statement allows to add a kind of sync for your firestore operations by waiting for the promises.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
exports.eaxmple = functions.https.onRequest(async (req, res) => {
var datax = []
var collections = await db.collection('collection').doc('docid').listCollections()
for (collection in collections) {
content = await collections[collection].get().then(querySnapshot => {
console.log('number of documents: ' + querySnapshot.size);
return querySnapshot.docs.map(doc => doc.data());
});
datax.push(content)
}
return res.json({datax});
});

Why does my update in a put request overwrite the whole record in Sequelize?

I'm trying to make a "edit" feature for my project, and I'm stuck at this part..
I have a put request :
export const updateEvent = (event, id) => (dispatch, getState) => {
request
.put(`${baseUrl}/event/${id}`)
.send(event)
.then(response => {
dispatch(updatedEvent(response.body))
})
.catch(error => console.log(error))
}
This is the route for the said put, with Sequelize as ORM:
router.put('/event/:id', async (req, res, next) => {
const { id } = req.params
try {
const event = await Event.findByPk(id)
const updatedEvent = await event.update(req.body)
res.send(updatedEvent)
} catch (error) {
next(error)
}
})
When I test it with postman, everything works as expected. Where I ran into my problem is when I'm sending the put data from React in the frontend.
I have a form, and I save my data in the local state, and then dispatch it to actions like this:
handleSubmit = e => {
e.preventDefault()
const id = this.props.event.id
const updatedEvent = {
name: this.state.name,
description: this.state.description,
picture: this.state.picture,
startDate: this.state.startDate,
endDate: this.state.endDate,
userId: this.props.userId
}
this.props.updateEvent(updatedEvent, id)
}
Any value that is left empty in the form is overwriting my fields with nothing (an empty string). How do I properly handle this?
A solution is to filter your object, such that you remove any properties which have empty values and therefore won't be included in the database update.
In your router.put():
router.put('/event/:id', async (req, res, next) => {
const { id } = req.params
try {
const event = await Event.findByPk(id);
// filter req.body to remove empty values
const { body } = req;
const filteredBody = Object.keys(body).reduce((resultObj, key) => {
if(body[key] != ''){
resultObj[key] = body[key];
}
return resultObj;
}, {});
const updatedEvent = await event.update(filteredBody);
res.send(updatedEvent)
} catch (error) {
next(error)
}
})

React Native - Invariant Violation: Objects are not valid as a React Child (Using Node JS for backend)

here I'm trying to have the sum of orders and the sum of their quantity in which I use Node JS for my backend. The problem is whenever I run my code -- my fetch functions seems not working properly or I'm missing something that I'm not aware.
But using postman, my API is working with the expected output. Buuut if I use it in my react-native code it show some errors.
Here's my code for backend:
OrderNo.js (models) //Backend
var Task = {
Sum:function(id,callback) {
return db.query("SELECT SUM(order_amount) AS TotalAmountOrdered FROM orders where order_no=?",[id],callback);
},
}
OrderNo.js (router) //Backend
var Task = require('../models/OrderNo');
router.get('/ForSum/:id?', (req, res, next) => {
Task.Sum(req.params.id,function(err,rows) {
if(err) {
res.json(err);
}
else {
res.json(rows);
}
});
});
NumOrder.js (models) //Backend
var Task = {
NumOrder:function(id,callback) {
return db.query("SELECT SUM(order_quantity) AS TotalItemsOrdered FROM orders where order_no=?",[id],callback);
},
}
NumOrder.js (router) //Backend
var Task = require('../models/NumOrder');
router.get('/num/:id?', (req, res, next) => {
Task.NumOrder(req.params.id,function(err,rows) {
if(err) {
res.json(err);
}
else {
res.json(rows);
}
});
});
And here's my code for React-Native
export default class Settlement extends Component {
constructor(props){
super(props)
this.state = {
orderDet: this.props.navigation.state.params.orderDet,
numOrder: [],
TotalSum: [],
};
}
fetchSum = async () => {
const response = await fetch("http://192.168.254.104:3308/OrderNo/ForSum/" + this.state.orderDet)
const json = await response.json()
this.setState({ TotalSum: json })
}
fetchNumOrders = async () => {
const response = await fetch("http://192.168.254.104:3308/NumOrder/num/" + this.state.orderDet )
const json = await response.json()
this.setState({ numOrder: json })
}
componentDidMount() {
this.fetchNumOrders();
this.fetchSum();
}
render() {
return (
<View>
<Text>Number of Orders: { this.state.numOrder }</Text>
<Text>Total Amount: ₱{ this.state.TotalSum }</Text>
</View>
)
}
}
And here is my DB
**PS: **I also tried " json[0].order_no " on each of my fetch function and there's no error, but my output is empty.
Based on your response object in the Postman, you need to do the following
this.state = {
orderDet: this.props.navigation.state.params.orderDet,
numOrder: null,
TotalSum: null,
};
fetchSum = async () => {
const response = await fetch("http://192.168.254.104:3308/OrderNo/ForSum/" + this.state.orderDet)
const json = await response.json()
this.setState({ TotalSum: json[0].TotalAmountOrdered })
}
fetchNumOrders = async () => {
const response = await fetch("http://192.168.254.104:3308/NumOrder/num/" + this.state.orderDet )
const json = await response.json()
this.setState({ numOrder: json[0].TotalItemsOrdered })
}
The error means that you cannot have object or array as the child the component i.e. <Text>. You can only have string or number displayed inside the component.
<Text>Number of Orders: { this.state.numOrder[0].TotalAmountOrdered }</Text>// Inside {} value of variable should be string or number not array or object
The error is that you are setting value of this.state.numOrder an array

Correct way to pass async eerror

I have a function which uses Firebase auth to update a user's email:
export const updateEmail = async (email) => {
const user = auth.currentUser;
return user.updateEmail(email);
};
It is used in a function which gets an email from a form (in React) and tries to update the email. If there is an error, we change the state to reflect that.
handleSave = (e) => {
const email = e.target.email.value;
updateEmail(email).catch((err) => {
this.setState({ didError: true, emailError: err.message });
});
};
However, when an error occurs, in the console I get:
My question is: why does this still say 'Uncaught'? Does the .catch() in handleSave not take care of that?
update
Link to relevant Firebase docs
Assuming updateEmail returns a prmise, I guess you can try:
export const updateEmail = (email) => { // no need for async here
const user = auth.currentUser;
return user.updateEmail(email);
};
handleSave = async (e) => {
const email = e.target.email.value;
try{
await updateEmail(email);
}catch(err){
this.setState({ didError: true, emailError: err.message });
}
};
I'm not quite sure since I don't know so much about Firebase, let me suggest something.
export const updateEmail = async (email) => {
const user = auth.currentUser;
const response = await user.updateEmail(email);
if ( response.error ) {
throw new Error( response.error );
}
return "something else";
};

Code not executed after PUT route using Express

I listen to the chat event of the tmijs library, upon the !overlay chat I want to execute some code. What I want to achieve upon getting that message is:
Fetch the user
Check if the user has enough currency
Deduct currency from the user
Trigger a socket event to my react app
Everything seems to work up until the last bullet point. In my terminal it's shown that my user gets currency (called 'kluiten' in my code) deducted, but all the code that comes after it doesn't get executed.
require('dotenv').config();
const PORT = process.env.PORT || 9000;
class TwitchAPI {
constructor({io}) {
this.io = io;
this.client = new tmi.client(options);
this.client.connect();
this.handleOverlayRequest = this.handleOverlayRequest.bind(this);
this.handleChatMessage = this.handleChatMessage.bind(this);
this.client.on('chat', this.handleChatMessage);
}
handleChatMessage (channel, userstate, message) {
if(message === '!overlay') this.handleOverlayRequest(channel, userstate);
}
async handleOverlayRequest (channel, userstate) {
const requiredKluiten = 5;
const rawFoundUser = await fetch(`http://localhost:${PORT}/api/users/${userstate.username}`);
const foundUser = await rawFoundUser.json();
if(foundUser.instakluiten >= requiredKluiten) {
this.client.action(channel, `${userstate[`display-name`]}, you've got enough instakluiten for this.`);
const method = `PUT`;
const payload = { 'requiredKluiten': requiredKluiten };
const body = JSON.stringify(payload);
const headers = { 'Content-Type': `application/json; charset=utf-8` };
const result = await fetch(`http://localhost:${PORT}/api/users/${userstate.username}/decrementKluiten`, { method, body, headers });
console.log(result);
}
}
}
module.exports = TwitchAPI;
I then have an Express router:
const express = require('express');
const userController = require('../controllers/userController');
const router = express.Router();
router.route('/users/:username/decrementKluiten').put(userController.decrementKluiten);
router.route('/users/:username').get(userController.getUser);
router.route('/overview').get(userController.getOverview);
module.exports = router;
which makes sure the currency gets deducted. What I'm stuck on now is that, after all this has happened, I can't execute any code anymore after the fetch. I found though that I could execute code by resolving the promise in my route, but that feels really dirty and messes up my split up files:
router.route('/users/:username/decrementKluiten').put((req, res) => {
userController.decrementKluiten(req, res).then(x => {
console.log(x);
});
});
Is there a way to wait for my PUT to happen and still execute code after it did?
EDIT
userController.js
const {findChattersPerRole, getUserByUsername, decrementKluiten} = require('../actions');
const find = require(`lodash/find`);
const fetch = require(`isomorphic-fetch`);
const parseJSON = response => response.json();
module.exports = {
getUser: (req, res) => {
const username = req.params.username;
findChattersPerRole()
.then(chattersPerRole => {
const wantedUser = find(chattersPerRole, { username });
getUserByUsername(wantedUser.username)
.then(foundUser => {
if (foundUser) {
res.send(foundUser);
} else {
res.send(`No user has been found`);
}
});
});
},
getOverview: (req, res) => {
fetch(`https://tmi.twitch.tv/group/user/instak/chatters`)
.then(parseJSON)
.then(r => {
return res.json(r);
}).catch(err => {
console.log(err);
});
},
decrementKluiten: (req, res) => {
decrementKluiten(req.params.username, req.body.requiredKluiten);
}
}
actions.js
(Because this contains a lot of code I try to only include the relevant parts for this post, the database calls are done using Sequelize.js)
const decrementKluiten = (username, requiredKluiten) => {
return global.db.Viewer.findOne({
where: { username }
}).then(user => {
return user.decrement({ instakluiten: requiredKluiten });
});
};
module.exports = {
decrementKluiten
};
The issue is likely that you don't respond to the HTTP request in your /users/:username/decrementKluiten route. To solve this, change the exported decrementKluiten method in userController.js-file to this:
decrementKluiten: (req, res) => {
decrementKluiten(req.params.username, req.body.requiredKluiten)
.then(() => res.sendStatus(200))
.catch(() => res.sendStatus(500));
}
Some unrelated pointers to make your code a bit more readable, since you already use async functions in some parts of your code, but in other parts you interface directly with Promises.
The exported part of userController.js could utilize async functions:
module.exports = {
getUser: async (req, res) => {
try {
const username = req.params.username;
let chattersPerRole = await findChattersPerRole();
let wantedUser = find(chattersPerRole, { username });
let foundUser = await getUserByUsername(watnerUser.username);
if (foundUser) {
res.status(200).send(foundUser);
} else {
res.status(404).send('No user has been found');
}
} catch (e) {
res.sendStatus(500);
}
},
getOverview: async (req, res) => {
try {
let r = (await fetch('https://tmi.twitch.tv/group/user/instak/chatters')).json();
res.json(r);
} catch (e) {
res.sendStatus(500);
}
},
decrementKluiten: async (req, res) => {
try {
await decrementKluiten(req.params.username, req.body.requiredKluiten);
res.sendStatus(200);
} catch (e) {
res.sendStatus(500);
}
}
}
I've also added error handling in case something goes wrong, the server responds with a 500 Internal Server Error status code.
Judging by these lines in your TwitchAPI class:
const rawFoundUser = await fetch(`http://localhost:${PORT}/api/users/${userstate.username}`);
const foundUser = await rawFoundUser.json();
I assume you've tried to do const foundUser = await fetch('...').json(). This results in an error, but you can call the retuned value's methods and properties on the same line if you wrap the await expression in parentheses, like this:
const foundUser = await (await fetch('...')).json()`
If its methods does not return a Promise (i.e being synchronous), or you want to access a property, you can do:
const something = (await doSomethingAsync()).someMethod()
const somethingElse = (await doSomethingAsync()).property
I also noticed you're using template literals (backticks, `) for most strings without doing any template interpolation, which could simply be replaced with ' (single-quotes) or " (double-quotes).

Categories