.map() React undefined - Axios & Express - node.js

Keep getting this error when trying to submit a form in React
Uncaught TypeError: queryList.map is not a function
This is the code:
const [query, setQuery] = useState();
const [queryList, setQueryList] = useState([]);
const [response, setResponse] = useState([]);
const [responseList, setResponseList] = useState([]);
const createQuery = () =>{
setQueryList(
{query}
);
alert(queryList)
Axios.post('http://localhost:3001/createQuery', {
query
}).then((res)=>{
setResponse(res)
setResponseList(...responseList, {res})
})
}
return(
<div>
{queryList && queryList.map((e) => {
return(
<p className="ml-52 text-white text-xl">{e.query}</p>
)
})}
<form>
<textarea onChange={(event)=>{
setQuery(event.target.value)
}}
type="text" name="name" autoComplete="off" placeholder="Ask a question" className = "caret-gray-200 bg-gray-800 shadow-md h-20 w-5/6 inset-x-0 bottom-6 absolute left-36 items-center snap-center text-xl p-6" />
<button onClick={createQuery} type="submit" name="submit" className="text-white inset-x-0 bottom-6 absolute bg-transparent w-20 h-20 ml-auto mr-28 focus:outline-none focus:none">
<AiOutlineSend size="28" />
</button>
</form>
</div>
)
After I submit the form, I need the query mapped
I know I didn't mention any other errors, but if you see them, please let me know!
By the way, all the other posts didn't work for me.
Thanks :)

When you set the queryList you will need to set it to an array by using array brackets. Also by the name i'm assuming you want an array of all queries so you will need to include previous queries that are already stored in queryList.
setQueryList([...queryList, { query }]);
This is what your setter function should look like.
Aside from this the alert function will not work since queryList is not updated in time to be used so I would recommend you to just use query in alert instead of queryList.
Also because of the way you use forms the page will be redirected, this is solved by using onSubmit event and using the preventDefault() function.
const [query, setQuery] = useState();
const [queryList, setQueryList] = useState([]);
const [response, setResponse] = useState();
const [responseList, setResponseList] = useState([]);
const createQuery = (event) => {
event.preventDefault();
setQueryList([...queryList, { query }]);
alert(query);
Axios.post('http://localhost:3001/createQuery', {
query,
}).then((res) => {
setResponse(res);
setResponseList(...responseList, { res });
});
};
return (
<div>
{queryList &&
queryList.map((e) => {
return (
<p className='ml-52 text-white text-xl'>{e.query}</p>
);
})}
<form onSubmit={createQuery}>
<textarea
onChange={(event) => {
setQuery(event.target.value);
}}
type='text'
name='name'
autoComplete='off'
placeholder='Ask a question'
className='caret-gray-200 bg-gray-800 shadow-md h-20 w-5/6 inset-x-0 bottom-6 absolute left-36 items-center snap-center text-xl p-6'
/>
<button
type='submit'
name='submit'
className='text-white inset-x-0 bottom-6 absolute bg-transparent w-20 h-20 ml-auto mr-28 focus:outline-none focus:none'
/>
</form>
</div>
);
I have not tested anything with axios but this code should work.

Related

Save a specific value from an array of object and store them individually into another state to get the sum ReactJS

In addition to this question
I am trying to map individually a state to another state to store the amountToPay object to get the sum of it. The problem is every time it renders the onChange function. It stores every state as object as you can see here: .
What I want to happen is to only get [434] instead of ['','4','43','434']
So I can .reduce the array to get the sum.
My method on storing the array object to another state is this
const [amountToPay, setAmountToPay] = useState("")
console.log("AMOUNT TO PAY", amountToPay)
useEffect(() => {
serviceItem.map((item) => (
setAmountToPay([...amountToPay, item.amountToPay])
))
}, [serviceItem])
useEffect(() => {
serviceItem.map((item) => (
setAmountToPay([...amountToPay, item.amountToPay])
))
}, [serviceItem])
You can check the whole code here CodeSandbox code.Any help is appreciated :)
There are several things I suggest you to do:
Add some id property to your serviceItem. You can use UUID, nanoid, or even Date.now()
Remove const [amountToPay, setAmountToPay] = useState([]);
Use values directly from serviceItem collection. In order to do this you need to create onChange handler, it will be something like this
const handleChange = (id) => (nextAmount) => {
setServiceList(prevValue => {
return prevValue.map(item => item.id === id ? { ...item, amount: nextAmount } : item)
})
}
And amount to pay can be easily got from serviceItem collection, without effects or other states
const procedurePriceTotal = serviceItem.reduce(
(acc, item) => (acc = acc + item.amount),
0
);
this is happening because you are setting serviceItem on onChange method
and use passed serviceItem as deps array to useeffect in which you are setting amountToPay.
so on every change it's appending in array
Rather then setting amount in useEffect, make a method and call on remove/add button so it will only call after user is finished typing. you can also place a button 'Save' or 'calculate Amount' and call handleSetAmountToPAY method which will update amount.
import React, { useState, useMemo, useEffect } from "react";
export default function App() {
//Values
const [serviceItem, setServiceList] = useState([
{ serviceValue: "", quantityValue: "", amountToPay: "" }
]);
console.log("SERVICE ITEM", serviceItem);
//Add item function
const handleItemAdd = () => {
setServiceList([
...serviceItem,
{ serviceValue: "", quantityValue: "", amountToPay: "" }
]);
handleSetAmountToPAY(serviceItem)
};
//Remove item function
const handleItemRemove = (index) => {
const list = [...serviceItem];
list.splice(index, 1);
setServiceList(list);
handleSetAmountToPAY(list)
};
//Get Values
const handleGetValues = (e, index) => {
const { name, value } = e.target;
const list = [...serviceItem];
list[index][name] = value;
setServiceList(list);
};
//Saving state to another state
const [amountToPay, setAmountToPay] = useState([]);
console.log("AMOUNT TO PAY", amountToPay);
const handleSetAmountToPAY = (list) => {
list && list.map((item) =>
setAmountToPay([...amountToPay, item.amountToPay])
);
}
//Add total amount
const procedurePriceTotal = amountToPay.reduce(
(index, value) => (index = index + value),
0
);
console.log("TOTAL PRICE", procedurePriceTotal);
return (
<div className="App">
{serviceItem.map((singleItem, index) => (
<div class="row form-row">
<div class="col-12 col-md-6 col-lg-4">
<div class="form-group">
<label>
Service <span class="text-danger">*</span>
</label>
<input
name="serviceValue"
type="text"
class="form-control"
value={singleItem.serviceValue}
placeholder="Tooth Extraction"
onChange={(e) => {
handleGetValues(e, index);
}}
/>
</div>
</div>
<div class="col-12 col-md-6 col-lg-3">
<div class="form-group">
<label>
Quantity <span class="text-danger">*</span>
</label>
<input
name="quantityValue"
type="text"
class="form-control"
placeholder="1"
value={singleItem.quantityValue}
onChange={(e) => {
handleGetValues(e, index);
}}
/>
</div>
</div>
<div class="col-12 col-md-6 col-lg-3">
<div class="form-group">
<label>
Amount (₱)<span class="text-danger">*</span>
</label>
<input
name="amountToPay"
type="number"
class="form-control"
placeholder="500"
value={singleItem.amountToPay}
onChange={(e) => {
handleGetValues(e, index);
}}
/>
</div>
</div>
<div class="col-12 col-md-6 col-lg-2">
<div class="add-more">
<br />
{serviceItem.length !== 1 && (
<button
type="submit"
onClick={() => handleItemRemove(index)}
className="btn btn-primary rx-pr"
>
<i className="fas fa-plus" /> Remove Item
</button>
)}
</div>
</div>
</div>
))}
{/* Add Item */}
<div className="add-more-item rx-pr">
<button
type="submit"
onClick={handleItemAdd}
className="btn btn-primary rx-pr"
>
<i className="fas fa-plus" /> Add Item
</button>
</div>
</div>
);
}
I was doing it the wrong way.
I solved it by mapping the serviceItem then using reduce to get the sum instead of putting it again into a separate array of object then mapping it again to get the sum.
const newNumberArray = serviceItem.map(function(item) {
return parseInt(item.amountToPay)
})
const totalAmountPaid = newNumberArray.reduce((index,value) => index = index + value, 0 )
Thanks for all the help and suggestion!

How can I display my response messages that I send in node js on my frontend with Reactjs

I am trying to build a basic login/signup app with the mern stack.I am new to it. While making the backend with node and express I have added lot of json messages in the code like-
return res.status(400).json({message:"Unable to find user by this id"})
return res.status(404).json({message:"User doesnt exist"});
How can I display this json data to my frontend with react?
This is the handleSubmit code-
const handleSubmit=(e)=>{
e.preventDefault()
console.log(inputs);
if(isSignup){
sendRequest("signup")
.then((data)=>localStorage.setItem("userId",data.user._id))
.then(()=>dispath(authActions.login()))
.then(()=>navigate("/blogs"))
}else{
sendRequest()
.then((data)=>localStorage.setItem("userId",data.user._id))
.then(()=>dispath(authActions.login()))
.then(()=>navigate("/blogs"))
.then(data=>console.log(data))
}
}
This is the return div code-
<form onSubmit={handleSubmit}>
<Box display="flex" flexDirection={'column'}
alignItems='center'
maxWidth={400}
justifyContent={'center'}
boxShadow="10px 10px 20px #ccc"
padding={3}
margin="auto"
marginTop={5}
borderRaidus={5}>
<Typography variant="h2" padding={3} textAlign="center">{isSignup?"Signup":"Login"}</Typography>
{isSignup && <TextField name="name" onChange={handleChange} value={inputs.name} placeholder='name' margin="normal"/>}
<TextField name="email" onChange={handleChange} value={inputs.email} type={'email'} placeholder='email' margin='normal'/>
<TextField name="password" onChange={handleChange} value={inputs.password} type={'password'} placeholder='password' margin='normal'/>
<Button type={"submit"} variant="contained" sx={{borderRadius:3,marginTop:3}} color="warning">Submit</Button>
<Button onClick={()=>setIsSignup(!isSignup)} sx={{borderRadius:3,marginTop:3}}>
Change to {isSignup ?"Login":"Signup"}</Button>
</Box>
</form>
</div>
)
}
export default Auth
Just try this one.
const handleSubmit = async(...) => {
...
try{
const response = await axios.post(...);
const data = await response.json();
console.log(data);
}catch(){
...
}
}

How to get form data as a object in reactjs

I'm trying to create a google form with react,
I have been creating all the questions as a components
if (props.type == "text") {
return (
<div className="box">
<h3 className="">{props.qustion}</h3>
<input className="short-text" placeholder="Your answer" id={"text"+props.id} name={"q"+props.id} type="text" onChange={updateField}/>
</div>
)
}
else if (props.type == "choice") {
return (
<div className="box">
<h3 className="">{props.qustion}{props.requre? <label className="requir">*</label>:""}</h3>
{props.answer.map(ans=>(
<div key={ans}>
<input className="radio" type="radio" id={ans} name={"radio"+props.id} value={ans} required={props.requre} onChange={updateField}/>
<label htmlFor={ans}>{ans}</label>
</div>
))
}
</div>
)
and I have been creating a form on the app file and put the components inside him,
return (
<div className="App">
<FormTitle/>
<form>
{
error? <h1>the sorce not found</h1>:data.map((item) =>(<Qustion qustion={item.question} type={item.type} requre={item.requre} id={item.id} answer={item.answares} key={item.id} />))
}
<div className="submit-right">
<input className="submit-button" type="submit" value="Submit" />
</div>
</form>
</div>
);
how to get all the form data as an object to create a post request ??
Try this function at start of the file where the form is
const formSubmit = (event) => {
event.preventDefault();
var data = new FormData(event.target);
let formObject = Object.fromEntries(data.entries());
console.log(formObject);
}
and in the form use this onSubmit={formSubmit}
<form onSubmit={formSubmit}>
<any element or components>
</form>
entries is not a function you can just reach it
const formSubmit = (event) => {
event.preventDefault();
var data = new FormData(event.target);
let formObject = Object.fromEntries(data.entries);
console.log(formObject);
}

Trying to display IMG with src set to Binary/Buffer image data from Mongoose

Trying to display IMGs with React/JSX with Buffered/Binary data saved to my MongoDB/Mongoose database.
i iterate over the data array with the IMG element looking like this:
<img src={`data:${item.img.contentType};base64,${item.img.data.data.toString("base64")}`} alt="" />
import React, { useState, useEffect } from "react";
import axios from "axios";
const FilesUpload = () => {
const [allPics, setAllPics] = useState([]);
useEffect(() => {
const getPics = async () => {
let res = await axios.get("http://localhost:8080/");
setAllPics(res.data);
};
// query the server for all of the picture objects
getPics();
}, []);
const [state, setState] = useState({});
const handleChange = (e) => {
e.preventDefault();
setState(e.target.value);
console.log(state);
};
return (
<>
<h1>upload an image</h1>
<hr />
<div>
<form
action="http://localhost:8080/"
method="POST"
encType="multipart/form-data"
>
<div>
<label>Image Title</label>
<input type="text" id="name" placeholder="Name" name="name" onChange={handleChange} value={state.name}/>
</div>
<div>
<label htmlFor="desc">Image Description</label>
<textarea id="desc" name="desc" rows="2" placeholder="Description"
onChange={handleChange} value={state.desc}/>
</div>
<div>
<label htmlFor="image">Upload Image</label>
<input type="file" id="image" name="image" required />
</div>
<div>
<button type="submit">Submit</button>
</div>
</form>
</div>
{allPics.length > 0 ? (
<div>
{allPics.map((item, index) => (
<div key={index}>
<div>
<img src={`data:${item.img.contentType};base64,${item.img.data.data.toString("base64")}`} alt="" />
</div>
</div>
))}
</div>
) : (
<>
<hr />
<br />
<h1>uploaded files</h1>
<h5>Loading..</h5>
</>
)}
</>
);
};
export default FilesUpload;
but I always get ERR_INVALID_URL:
from similar threads on the net I've read that I need to take those comma-delimitated values and remove the comma which will give me the proper data. having a hard time figuring that out. any help would be great. thanks
I was facing the same problem after saving image like this in my mongoose model and after many research I resolved this, hope this works for you also
const base64String = btoa(String.fromCharCode(...new Uint8Array(item.img.data.data)));
and in img tag put this -:
src={`data:image/${item?.img?.contentType};base64,${base64String}`}

How to create onSubmit with Material-UI

So I'm fairly new to node.js / react / material-ui. I've been following a guide to try setting up a website and have been getting on pretty well. I decided to include material-ui for snazzy components (not part of the guide) and then got stuck because I can't seem to fire custom functions whilst using a themed ui.
With the below code, I can create everything fine if I ditch the 'classes' props. I can add my function and everything works. But I obviously lose all my styling if I do this.
const styles = theme => ({
// Styling - omitted
});
const Login = (props) => {
const {classes} = props;
return(
<div>
<Paper className={classes.root}>
<form className={classes.container} noValidate autoComplete="off">
<TextField
id="email"
label="Email"
className={classes.textField}
InputProps={{
className: classes.input
}}
type="email"
name="email"
autoComplete="email"
margin="normal"
variant="outlined"
required
autoFocus
/>
<TextField
id="outlined"
label="Password"
className={classes.textField}
InputProps={{
className: classes.input
}}
type="password"
autoComplete="current-password"
margin="normal"
variant="outlined"
required
/>
<Typography className={classes.divider} />
<Button
type="submit"
variant="contained"
color="inherit"
className={classes.button}
>
Login
</Button>
</form>
</Paper>
</div>
);
}
Login.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(Login);
I'm trying to combine the styling as well as being able to fire a custom function to submit the form data. Does anyone have any thoughts?
The class property/ styles shouldn't have any effect on form submission custom function. If you think that ditching the 'class props' you can get a custom function to work - post your code so we can help you further. [Your code is missing submit function]
Here is one way to add custom submit function
const Login = (props) => {
const {classes} = props;
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
function handleSubmit(event) {
event.preventDefault();
console.log( 'Email:', email, 'Password: ', password);
// You should see email and password in console.
// ..code to submit form to backend here...
}
return( <div >
<Paper className={classes.root}>
<form className={classes.container} onSubmit={handleSubmit} >
<TextField
....
value={email}
onInput={ e=>setEmail(e.target.value)}
.....
/>
<TextField
....
value={password}
onInput={ e=>setPassword(e.target.value)}
....
/>
<Typography className={classes.divider} />
<Button
type="submit"
....
className={classes.button}
>
Login
</Button>
</form>
</Paper>
</div>
);

Resources