Submit React form with multiple text input and image - node.js

So, from this link REACT : How can we Upload Image and Input text using Fetch or Axios in a single click function , i learn how to submit form with text + images, but it only contains 1 text input, from my case, i have multiple text input, How i submit multiple text input with formData ? i tried use Object, but it cannot be use on server side.
Edit: So i got it using this, but is there a way to send the whole object directly?
async function submitForm() {
const formdata = new FormData(); // for adding form files i guess
for (let i = 0; i < files.length; i++) {
formdata.append("files", files[i], files[i].name); // "name", files, filename
}
for (let key in inputs) {
formdata.append(key, inputs[key]);
}
const response = await axios.post("/games", formdata);
const { data } = response;
console.log(data.message);
}
here is my code
const AddForm = ({ getData, classes }) => {
console.log("Rendering AddFORM");
const [checkboxes, setChecked] = React.useState([]);
const [inputs, setInputs] = React.useState({});
const [files, setFiles] = React.useState(null);
const history = useHistory();
React.useEffect(() => {
setInputs({ ...inputs, platform: checkboxes });
}, [checkboxes]);
const onChangeForField = React.useCallback(({ target: { name, value } }) =>
setInputs((state) => ({ ...state, [name]: value }), [])
);
const onChangeForFiles = ({ target: { files } }) => setFiles(files);
const handleCheck = ({ target: { value } }) => {
checkboxes.includes(value)
? setChecked(checkboxes.filter((item) => item !== value))
: setChecked([...checkboxes, value]);
};
async function submitForm() {
const formdata = new FormData(); // for adding form files i guess
for (let i = 0; i < files.length; i++) {
formdata.append("files", files[i], files[i].name); // "name", files, filename
}
formdata.set("data", inputs);
const response = await axios.post("/games", formdata);
const { data } = response;
console.log(data.message);
}
return (
<Paper
elevation={2}
style={{ padding: "1rem", width: "70%", margin: "0 auto" }}
>
<form>
<Typography variant="h5" color="initial">
Add a new game
</Typography>
<TextField
required
id="standard-required"
name="title"
label="Title"
fullWidth
placeholder="Game title"
margin="normal"
onChange={onChangeForField}
/>
<TextField
fullWidth
multiline
required
id="standard-required"
name="description"
label="Description"
placeholder="Description"
margin="normal"
onChange={onChangeForField}
/>
<FormControl margin="normal" fullWidth>
<FormLabel component="legend">Select Platforms</FormLabel>
<FormGroup row>
{platforms.map((p, idx) => (
<FormControlLabel
key={idx}
control={
<Checkbox name="platforms" onChange={handleCheck} value={p} />
}
label={p}
/>
))}
</FormGroup>
</FormControl>
<FormControl>
<Button variant="contained">
<label htmlFor="file-upload">Upload Files</label>
</Button>
<input
type="file"
multiple
id="file-upload"
name="images"
style={{ display: "none" }}
onChange={onChangeForFiles}
></input>
</FormControl>
<div className={classes.ButtonsContainer}>
<Button
variant="contained"
color="primary"
onClick={async (e) => {
e.preventDefault();
await submitForm();
getData();
// history.push("/");
}}
>
Submit
</Button>
<Button variant="contained" color="secondary">
<Link to="/">Go Back</Link>
</Button>
</div>
</form>
</Paper>
);
};
server side route
app.post(
"/games",
upload.array("files"),
wrapAsync(async (req, res) => {
console.log(req.body);
console.log(req.files);
got [object Object] on server side, looks like it cannot parse this.
Thanks before

Related

Can not load array from backend

After a search, I am sending the result to frontend in the form of array. But I am not being able to get that array in frontend using fetch. Through postman I am able to get the result from the backend but I am not able to get it in the frontend. In another file, I have set axios.post as well and exported from there and imported in frotend.
I am beginner so I might have written a bad code, any help will mean a lot.
In frontend :
class Hostel extends Component{
constructor (){
super();
this.state = {
country : '',
city : '',
category : '',
errors : {}
};
}
componentWillReceiveProps(nextProps) {
if (nextProps.errors) {
this.setState({
errors: nextProps.errors
});
}
}
onChangeAddOptions = e => {
this.setState({ [e.target.id]: e.target.value });
};
addOption = e => {
e.preventDefault();
const newOption = {
country : this.state.country,
city : this.state.city,
category:this.state.category,
}
this.props.saveOptions(newOption,this.props.history);
};
getHostels = async ()=> {
console.log("getHostel function is called");
const response = await fetch('http://localhost:5000/api/users/hostel',{
method : "POST",
// headers:{
// "Content-Type" : "application/json"
// },
})
.then((response)=> {response.json()})
.then((data)=>{
console.log("inside data");
console.log(data);
})
.catch(e=>{
console.error(e.error);
})
console.log("From outside of data");
console.log(response);
}
componentDidMount(){
this.getHostels();
}
render (){
const {errors,country,city,category} = this.state;
return(
<section className="Hosteldashboard">
<div className="left_container">
<h2>Yo che left section</h2>
<div>
<form noValidate onSubmit={this.addOption}>
<div class="form-row">
<label htmlFor="country">Country</label> <br />
<input
type="text"
className="input-control"
placeholder="Country name"
id="country"
value={country}
onChange={this.onChangeAddOptions}
error={errors.country}
className={classnames('', {
invalid: errors.country
})}
/>{' '}
<br />
<span className="text-danger">{errors.country}</span>
</div>
<div class="form-row">
<label htmlFor="city">City</label> <br />
<input
type="text"
className="input-control"
placeholder="City name"
id="city"
value={city}
onChange={this.onChangeAddOptions}
error={errors.city}
className={classnames('', {
invalid: errors.city
})}
/>{' '}
<br />
<span className="text-danger">{errors.city}</span>
</div>
<div class="form-row">
<label htmlFor="category">Category</label> <br />
<input
type="text"
className="input-control"
placeholder="Boys or Girls"
id="category"
value={category}
onChange={this.onChangeAddOptions}
error={errors.category}
className={classnames('', {
invalid: errors.category
})}
/>{' '}
<br />
<span className="text-danger">{errors.category}</span>
</div>
<div>
<button type="submit" className = "searchHostel" onClick={this.getHostels}>
Search
</button>
</div>
</form>
</div>
</div>
In backend :
router.post('/hostel',async (req,res)=>{
try{
console.log(req.body);
const {
errors,
isValid
} = validateSearchHostelInput(req.body);
//Check Validation
// if (!isValid){
// return res.status(400).json(errors);
// }
const page = parseInt(req.query.page) - 1 || 0;
const limit = parseInt(req.query.limit) || 5;
const search = req.query.search || "";
let sort = req.query.sort || "price";
let category = req.query.category || "All";
const categoryOptions = [
req.body.country,
req.body.city,
req.body.category
]
category === "All"
? (category = [...categoryOptions])
: (category = req.query.category.split(","));
req.query.sort ? (sort = req.query.sort.split(",")) : (sort = [sort]);
let sortBy = {};
if(sort[1]) {
sortBy[sort[0]] = sort[1];
} else {
sortBy[sort[0]] = "asc";
}
const hostel = await Hostel.find({title: {$regex: search, $options: "i"}})
.where("category")
.in([...category])
.sort(sortBy)
.skip(page * limit)
.limit(limit);
// const total = await Hostel.countDocuments({
// category: {$in: [...category]},
// title: { $regex: search, $options: "i"},
// });
// const response = {
// error: false,
// total,
// page: page + 1,
// limit,
// categories: categoryOptions,
// hostel
//}
console.log("From Hostel : " + hostel);
res.status(200).json({hostel:hostel});
}catch(err){
console.log(err);
res.status(500).json({error:true,message:"Internal Server Error"});
}
});
module.exports = router;

Electron React, my form is not preventing default event

I've tried everything, I used the standard input, without success, I already changed the onSubmit to onClick, without success, I already put the function in the onClick button without success, it only works when I remove the input field of type file
I am using Electron React Boilerplate
https://github.com/electron-react-boilerplate/electron-react-boilerplate
import { Button, Pane, FileUploader, FileCard, Alert } from 'evergreen-ui';
import VideoSnapshot from 'video-snapshot';
import Input from 'renderer/components/Input';
import React from 'react';
import axios from 'axios';
import ContainerMenu from '../components/ContainerMenu';
const Admin = () => {
const [name, setName] = React.useState('');
const [genre, setGenre] = React.useState('');
const [path, setPath] = React.useState('');
const [preview, setPreview] = React.useState(null);
const [submitError, setSubmitError] = React.useState('');
const [progress, setProgress] = React.useState(0);
const [files, setFiles] = React.useState([]);
const [fileRejections, setFileRejections] = React.useState([]);
const handleChange = React.useCallback((files) => setFiles([files[0]]), []);
const handleRejected = React.useCallback(
(fileRejections) => setFileRejections([fileRejections[0]]),
[]
);
const handleRemove = React.useCallback(() => {
setFiles([]);
setFileRejections([]);
}, []);
const handleSubmit = async (e) => {
e.preventDefault();
setSubmitError('');
if (files.length === 0) {
setSubmitError('O arquivo não pode ser vazio');
return;
}
try {
const snapshoter = new VideoSnapshot(files[0]);
const previewSrc = await snapshoter.takeSnapshot();
if (previewSrc) setPreview(previewSrc);
} catch (error) {
console.log(error);
}
try {
// const previewData = new FormData();
// const previewObject = {
// preview: previewSrc,
// };
// previewData.append('file', previewObject);
// await axios.post('http://localhost:3000/api/upload', previewData);
const formData = new FormData();
formData.append('file', files[0]);
formData.append('name', name);
formData.append('genre', genre);
formData.append('path', 'videos');
const response = await axios.post(
'http://localhost:3000/api/upload',
formData,
{
onUploadProgress: (progressEvent) => {
const progress = (progressEvent.loaded / progressEvent.total) * 50;
setProgress(progress);
},
}
);
} catch (error) {
console.log(error);
setSubmitError(error.message);
}
};
return (
<div>
<ContainerMenu />
<div className="container">
<form
encType="multpart/form-data"
className="form-group"
onSubmit={handleSubmit}
>
<h2>Enviar Arquivos</h2>
<br />
<div>
<Input
type="text"
label="Nome"
placeholder="Nome ..."
onChange={setName}
value={name}
/>
</div>
<div>
<Input
type="text"
label="Gênero"
placeholder="Gênero ..."
onChange={setGenre}
value={genre}
/>
</div>
<div>
<Pane>
<FileUploader
label="Upload File"
description="You can upload 1 file. File can be up to 50 MB."
maxSizeInBytes={50 * 1024 ** 2}
maxFiles={1}
onChange={handleChange}
onRejected={handleRejected}
renderFile={(file) => {
const { name, size, type } = file;
const fileRejection = fileRejections.find(
(fileRejection) => fileRejection.file === file
);
const { message } = fileRejection || {};
return (
<FileCard
key={name}
isInvalid={fileRejection != null}
name={name}
onRemove={handleRemove}
sizeInBytes={size}
type={type}
validationMessage={message}
/>
);
}}
values={files}
/>
</Pane>
</div>
<div>
<Button
isLoading={progress > 0 && progress < 100}
marginRight={16}
appearance="primary"
type="submit"
>
Enviar {progress.toFixed(2)}%
</Button>
</div>
<br />
<div>{preview && <img src={preview} alt="preview" />}</div>
<br />
<div>
{submitError.length > 0 && (
<Alert intent="danger" title="Error">
{submitError}
</Alert>
)}
</div>
</form>
</div>
</div>
);
};
export default function App() {
return <Admin />;
}

api proplem and redux

i want to send data from client to api then database but i get error in the api that appears in console PUT http://localhost:5000/api/products/find/undefined 500 (Internal Server Error
the error started when i added the firebase part and i think the proplem is the api doesnt recieve ip
the api is
//edit
const updateProduct=async(req,res)=>{
try {
const updatedProduct = await Product.findByIdAndUpdate(
req.params.id,
{
$set: req.body,
},
{ new: true }
);
res.status(200).json(updatedProduct);
} catch (err) {
res.status(500).json(err);
}
}
client side is
import { Link, useLocation } from "react-router-dom";
import "./product.css";
import Chart from "../../components/chart/Chart";
import { Publish } from "#material-ui/icons";
import { useDispatch, useSelector } from "react-redux";
import { useEffect, useMemo, useState } from "react";
import { userRequest } from "../../requestMethods";
import { updateProduct } from "../../redux/apiCalls";
import {
getStorage,
ref,
uploadBytesResumable,
getDownloadURL,
} from "firebase/storage";
import app from "../../firebase";
export default function Product() {
const [user,setUser]=useState({});
const [file, setFile] = useState(null);
const location = useLocation();
const productId = location.pathname.split("/")[2];
const [pStats, setPStats] = useState([]);
const product = useSelector((state) =>
state.product.products.find((product) => product._id === productId)
);
const handleChange =(e)=>{
const value=e.target.value;
setUser({
...user,
[e.target.name]:value
});
};
console.log(user)
const dispatch = useDispatch();
const handleclick=(id)=>{
const fileName = new Date().getTime() + file.name;
const storage = getStorage(app);
const storageRef = ref(storage, fileName);
const uploadTask = uploadBytesResumable(storageRef, file);
uploadTask.on(
"state_changed",
(snapshot) => {
// Observe state change events such as progress, pause, and resume
// Get task progress, including the number of bytes uploaded and the total number of bytes to be uploaded
const progress =
(snapshot.bytesTransferred / snapshot.totalBytes) * 100;
console.log("Upload is " + progress + "% done");
switch (snapshot.state) {
case "paused":
console.log("Upload is paused");
break;
case "running":
console.log("Upload is running");
break;
default:
}
},
(error) => {
// Handle unsuccessful uploads
},
() => {
// Handle successful uploads on complete
// For instance, get the download URL: https://firebasestorage.googleapis.com/...
getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => {
const userr = { ...user, img: downloadURL};
updateProduct(id,dispatch,userr)
});
}
);
console.log(id)
}
useEffect(() => {
updateProduct(dispatch);
}, [dispatch]);
const MONTHS = useMemo(
() => [
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Agu",
"Sep",
"Oct",
"Nov",
"Dec",
],
[]
);
useEffect(() => {
const getStats = async () => {
try {
const res = await userRequest.get("order/income?pid=" + productId);
const list = res.data.sort((a,b)=>{
return a._id - b._id
})
list.map((item) =>
setPStats((prev) => [
...prev,
{ name: MONTHS[item._id - 1], Sales: item.total },
])
);
} catch (err) {
console.log(err);
}
};
getStats();
}, [productId, MONTHS]);
return (
<div className="product">
<div className="productTitleContainer">
<h1 className="productTitle">Product</h1>
<Link to="/newproduct">
<button className="productAddButton">Create</button>
</Link>
</div>
<div className="productTop">
<div className="productTopLeft">
<Chart data={pStats} dataKey="Sales" title="Sales Performance" />
</div>
<div className="productTopRight">
<div className="productInfoTop">
<img src={product.img} alt="" className="productInfoImg" />
<span className="productName">{product.title}</span>
</div>
<div className="productInfoBottom">
<div className="productInfoItem">
<span className="productInfoKey">id:</span>
<span className="productInfoValue">{product._id}</span>
</div>
<div className="productInfoItem">
<span className="productInfoKey">sales:</span>
<span className="productInfoValue">5123</span>
</div>
<div className="productInfoItem">
<span className="productInfoKey">in stock:</span>
<span className="productInfoValue">{product.inStock}</span>
</div>
</div>
</div>
</div>
<div className="productBottom">
<form className="productForm">
<div className="productFormLeft">
<label>Product Name</label>
<input type="text" placeholder={product.title} name="title" onChange={handleChange}/>
<label>Product Description</label>
<input type="text" placeholder={product.desc} name="desc" onChange={handleChange} />
<label>Price</label>
<input type="text" placeholder={product.price} name="price" onChange={handleChange} />
<label>In Stock</label>
<select name="inStock" id="idStock" onChange={handleChange}>
<option value="true">Yes</option>
<option value="false">No</option>
</select>
</div>
<div className="productFormRight">
<div className="productUpload">
<img src={product.img} alt="" className="productUploadImg" />
<label for="file">
<Publish />
</label>
<input type="file" id="file" style={{ display: "none" }} name="img" onChange={(e) => setFile(e.target.files[0])} />
</div>
<button className="productButton" onClick={(e)=>handleclick(e.preventDefault(),product._id)}>Update</button>
</div>
</form>
</div>
</div>
);
}
the update function
export const updateProduct = async (id, dispatch, product) => {
dispatch(updateProductStart());
try {
const res=await userRequest.put(`/products/find/${id}`,product)
dispatch(updateProductSuccess({ id, product }));
} catch (err) {
dispatch(updateProductFailure());
}
};
update success function
updateProductSuccess: (state, action) => {
state.isFetching = false;
state.products[
state.products.findIndex((item) => item._id === action.payload.id)
] = action.payload.product;
},
You're not calling handleclick with the expected arguments from the button with className "productButton".
Change the line to the following
<button className="productButton" onClick={(e)=> {
e.preventDefault();
handleclick(product._id);
}
}>Update</button>

How to send formData that includes image

I'm trying to send my form using React frontend to NodeJS backend and I'm not getting any data in formData() object, here is my code for React:
import { useState, useEffect } from 'react'
import Axios from 'axios'
import Notification from '../../../components/Notification'
const DashboardWorkAdd = ({ subTitle = 'works' }) => {
//Form States
const [workName, setWorkName] = useState('')
const [workGithub, setWorkGithub] = useState('')
const [workOnlineLink, setWorkOnlineLink] = useState('')
const [workDate, setWorkDate] = useState('')
const [workDesc, setWorkDesc] = useState('')
const [workAdded, setworkAdded] = useState('')
const [workAddedMsg, setworkAddedMsg] = useState('')
const [workImgFile, setWorkImgFile] = useState('')
const [preview, setPreview] = useState()
const formMsg = document.querySelector('.notification__msg')
const updateWorkImg = (e) => {
const file = e.target.files[0]
if (file) {
const fileType = file.type.split('/')[0]
const fileSizeToMB = file.size / 1000000
const MAX_FILE_SIZE = 1 //mb
if (formMsg) {
formMsg.classList.remove('hidden')
if (fileType !== 'image') {
formMsg.textContent = 'you can add only image file'
} else if (fileSizeToMB > MAX_FILE_SIZE) {
formMsg.textContent = `you can't add more than ${MAX_FILE_SIZE} MB`
return
} else {
formMsg.classList.add('hidden')
formMsg.textContent = ''
setWorkImgFile(file)
}
}
}
}
useEffect(() => {
// if there's an image
if (workImgFile) {
const reader = new FileReader()
reader.onloadend = () => setPreview(reader.result)
reader.readAsDataURL(workImgFile)
} else {
setPreview(null)
}
}, [workImgFile])
const handleAddWork = async (e) => {
e.preventDefault()
//using FormData to send constructed data
const formData = new FormData()
formData.append('workImg', workImgFile)
formData.append('workName', workName)
formData.append('workGithub', workGithub)
formData.append('workOnlineLink', workOnlineLink)
formData.append('workDate', workDate)
formData.append('workDesc', workDesc)
console.log(formData)
if (
(workName === '' || workGithub === '' || workOnlineLink === '' || workDate === '',
workDesc === '')
) {
formMsg.textContent = 'please add all data'
} else {
try {
const response = await Axios.post(
`${
process.env.NODE_ENV === 'development'
? process.env.REACT_APP_API_LOCAL_URL
: process.env.REACT_APP_API_URL
}/workAdd`,
{ workImgFile, workName, workGithub, workOnlineLink, workDate, workDesc }
)
const { workAdded, message } = response.data
setworkAdded(workAdded)
setworkAddedMsg(message)
} catch (err) {
console.error(err)
}
}
}
return (
<>
<h3 className='text-3xl mt-20 mb-12 text-center font-semibold'>{subTitle}</h3>
<div className='h-full'>
<div className='flex flex-col gap-3 py-4 text-sm font-semibold'>
<Notification sendStatus={workAdded} sendStatusMsg={workAddedMsg} />
<div className='notification__msg'></div>
<form
method='POST'
className='flex flex-col gap-14'
encType='multipart/form-data'
onSubmit={handleAddWork}
>
<label
htmlFor='workImg'
className='flex flex-wrap justify-center gap-5 md:justify-between items-center cursor-pointer'
>
<img
src={
preview === null ? 'https://source.unsplash.com/random?webdev' : preview
}
alt='Work Portfolio Preview'
className='w-36 h-36'
/>
<input
type='file'
name='workImg'
id='workImg'
className='bg-blue-500 py-6 px-28 rounded-lg text-white uppercase font-semibold cursor-pointer'
accept='image/*'
onChange={updateWorkImg}
multiple
required
/>
</label>
<div className='dashboard-group'>
<label htmlFor='workName'>work name</label>
<input
type='text'
id='workName'
autoFocus
onChange={(e) => {
setWorkName(e.target.value.trim())
}}
required
/>
</div>
<div className='dashboard-group'>
<label htmlFor='workLinkGithub'>Github Link</label>
<input
type='text'
id='workLinkGithub'
min='5'
max='500'
onChange={(e) => setWorkGithub(e.target.value.trim())}
required
/>
</div>
<div className='dashboard-group'>
<label htmlFor='workLinkOnline'>Online Linl</label>
<input
type='text'
id='workLinkOnline'
min='5'
max='500'
onChange={(e) => setWorkOnlineLink(e.target.value.trim())}
required
/>
</div>
<div className='dashboard-group'>
<label htmlFor='workDate'>Work Date</label>
<input
type='date'
id='workDate'
min='5'
max='500'
onChange={(e) => setWorkDate(e.target.value.trim())}
required
/>
</div>
<div className='dashboard-group'>
<label htmlFor='workDescription'>word description</label>
<textarea
name='workDescription'
id='workDescription'
minLength='10'
maxLength='300'
className=''
onChange={(e) => setWorkDesc(e.target.value.trim())}
required
></textarea>
</div>
<div className='flex justify-around text-lg'>
<button
type='submit'
className='bg-green-500 hover:bg-green-600 py-2 px-20 rounded-lg text-white transition-colors'
>
Add
</button>
</div>
</form>
</div>
</div>
</>
)
}
export default DashboardWorkAdd
Here is my code for NodeJS:
const WorksModel = require('../models/WorkModel')
const { v4: uuidv4 } = require('uuid')
module.exports = async (req, res) => {
const { workName, workGithub, workOnlineLink, workDate, workDesc } = req.body
const { workImg } = req.files
const workImgName = uuidv4() + workImg.name
const workImgMovePath = `${__dirname}/../../client/public/uploads/${workImgName}`
const workImgDisplayPath = `/uploads/${workImgName}`
const works = new WorksModel({
workImgDisplayPath,
workName,
workGithub,
workOnlineLink,
workDate,
workDesc
})
try {
await works.save()
workImg.mv(workImgMovePath, (err) => {
if (err) {
res.send({ message: `sorry, something wrong with the server: ${error}` })
return res.status(500).send(err)
}
})
res.send({
message: 'added succesfully',
workAdded: 1
})
} catch (error) {
res.send({
message: `something went wrong ${error}`,
workAdded: 0
})
}
}
and my WorkModel file is :
const mongoose = require('mongoose')
const WorkSchema = new mongoose.Schema({
workImgDisplayPath: {
type: String,
required: true
},
workName: {
type: String,
required: true
},
workGithub: {
type: String,
required: true
},
workOnlineLink: {
type: String,
required: true
},
workDate: {
type: Date,
required: true,
default: new Date().toLocaleDateString('ar-EG', {
weekday: 'long',
year: 'numeric',
month: 'short',
day: 'numeric'
})
},
workDesc: {
type: String,
required: true
}
})
const WorkModel = mongoose.model('mhmdhidrPortfolio', WorkSchema)
module.exports = WorkModel
the issue I'm having when I get the data in the backend is the req.files are undefined, I googled a lot and I don't know the issue exactly.
Thanks for your help.
I solved my problem, I just had to add:
const fileUpload = require('express-fileupload')
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
app.use(fileUpload())
inside my index.js (server) file

Unable to fetch data from Form using FormData

I am creating an 'edit profile' page for a dashboard the technologies that I use for the same are Next.js, Node.js & MongoDB.
Note: skip to the backend part if you just wanted to know the issue.
Frontend
Firstly,let me explain the Frontend part.
I am using useRef() inorder to reference data(name,bio) in the inputfields. which are working nicely.
Everything is fine the issue is in the handlesbumit() event_handler.
I am using FormData to send my form data to the backend API
If you're thinking why I'm not using a usual body object to send data the reason is that I have to add the profile picture updation later for which I have to send files , which as far I know we can't do that with an Object and yeah just to inform you it works fine if I would have used that Object part but can't use it with profilepicture updation.
The value that I have consoled out for the references are all good, and the rest of the handler is just as it is written can't find anything odd in that.
import { useUser } from '../../../lib/hooks';
import React, { useState, useEffect, useRef } from 'react';
import Head from 'next/head';
import { ImBook, ImListNumbered } from 'react-icons/im';
import { AiFillGithub, AiOutlineTwitter, AiFillFacebook, AiFillInstagram, AiFillLinkedin } from 'react-icons/ai'
import { FaFacebook, FaStackOverflow } from 'react-icons/fa';
const ProfileSection = () => {
const [user, { mutate }] = useUser();
const [isUpdating, setIsUpdating] = useState(false);
const nameRef = useRef();
const profilePictureRef = useRef();
const bioRef = useRef();
const [msg, setMsg] = useState({ message: '', isError: false });
useEffect(() => {
nameRef.current.value = user.name;
bioRef.current.value = user.Bio;
}, [user]);
const handleSubmit = async (event) => {
event.preventDefault();
if (isUpdating) return;
setIsUpdating(true);
console.log(nameRef.current.value); //Testing
console.log(bioRef.current.value); //Testing
const formData = new FormData();
formData.append('name', nameRef.current.value);
formData.append('Bio', bioRef.current.value);
console.log(formData.get('name'));
const res = await fetch('/api/user', {
method: 'PATCH',
body: formData,
});
if (res.status === 200) {
const userData = await res.json();
mutate({
user: {
...user,
...userData.user,
},
});
setMsg({ message: 'Profile updated' });
} else {
setMsg({ message: await res.text(), isError: true });
}
};
return (
<>
<Head>
<title>Settings</title>
</Head>
<main>
<div class="row">
<div class="col s12 m12">
<div className="card-panel br-10">
{msg.message ? <p style={{ color: msg.isError ? 'red' : '#0070f3', textAlign: 'center' }}>{msg.message}</p> : null}
<form onSubmit={handleSubmit}>
<div className="row">
<div className="col s12 m6 l6">
<label htmlFor="name">
Name
<input
required
id="name"
name="name"
type="text"
ref={nameRef}
/>
</label>
</div>
<div className="col s12 m6 l6">
<label htmlFor="bio">
Bio
<textarea
id="bio"
name="bio"
type="text"
ref={bioRef}
/>
</label>
</div>
</div>
<div className="center-align">
<button disabled={isUpdating} className="btn" type="submit" >Save</button>
</div>
</form>
</div>
</div>
</div>
</main>
</>
);
};
const SettingPage = () => {
const [user] = useUser();
if (!user) {
return (
<>
<p>Please sign in</p>
</>
);
}
return (
<>
<ProfileSection />
</>
);
};
export default SettingPage;
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
import { useUser } from '../../../lib/hooks';
import React, { useState, useEffect, useRef } from 'react';
import Head from 'next/head';
import { ImBook, ImListNumbered } from 'react-icons/im';
import { AiFillGithub, AiOutlineTwitter, AiFillFacebook, AiFillInstagram, AiFillLinkedin } from 'react-icons/ai'
import { FaFacebook, FaStackOverflow } from 'react-icons/fa';
const ProfileSection = () => {
const [user, { mutate }] = useUser();
const [isUpdating, setIsUpdating] = useState(false);
const nameRef = useRef();
const profilePictureRef = useRef();
const bioRef = useRef();
const [msg, setMsg] = useState({ message: '', isError: false });
useEffect(() => {
nameRef.current.value = user.name;
bioRef.current.value = user.Bio;
}, [user]);
const handleSubmit = async (event) => {
event.preventDefault();
if (isUpdating) return;
setIsUpdating(true);
console.log(nameRef.current.value);
console.log(bioRef.current.value);
const formData = new FormData();
formData.append('name', nameRef.current.value);
formData.append('Bio', bioRef.current.value);
console.log(formData.get('name'));
const res = await fetch('/api/user', {
method: 'PATCH',
body: formData,
});
if (res.status === 200) {
const userData = await res.json();
mutate({
user: {
...user,
...userData.user,
},
});
setMsg({ message: 'Profile updated' });
} else {
setMsg({ message: await res.text(), isError: true });
}
};
return (
<>
<Head>
<title>Settings</title>
</Head>
<main>
<div class="row">
<div class="col s12 m12">
<div className="card-panel br-10">
{msg.message ? <p style={{ color: msg.isError ? 'red' : '#0070f3', textAlign: 'center' }}>{msg.message}</p> : null}
<form onSubmit={handleSubmit}>
<div className="row">
<div className="col s12 m6 l6">
<label htmlFor="name">
Name
<input
required
id="name"
name="name"
type="text"
ref={nameRef}
/>
</label>
</div>
<div className="col s12 m6 l6">
<label htmlFor="bio">
Bio
<textarea
id="bio"
name="bio"
type="text"
ref={bioRef}
/>
</label>
</div>
</div>
<div className="center-align">
<button disabled={isUpdating} className="btn" type="submit" >Save</button>
</div>
</form>
</div>
</div>
</div>
</main>
</>
);
};
const SettingPage = () => {
const [user] = useUser();
if (!user) {
return (
<>
<p>Please sign in</p>
</>
);
}
return (
<>
<ProfileSection />
</>
);
};
export default SettingPage;
Backend
Now, the backend API for the same handlesubmit() event_handler i.e. 'api/user'
Please ignore the handler, it's just a predefined middleware npm next-connect which itself checks what type of request is coming if its 'PATCH' it will run handler.patch.
The Issue is the value of name & Bio is undefined,which means its not getting values from req.body;
And to check I also consoled out req.body which give out this
The data is correct but req.body is not a Object its a String now and I get it, its because I'm using formdata so how to get the values of name & Bio from this req.body ?
import nextConnect from 'next-connect';
import middleware from '../../../middlewares/middleware';
import { extractUser } from '../../../lib/api-helpers';
const handler = nextConnect();
handler.use(middleware);
handler.get(async (req, res) => res.json({ user: extractUser(req) }));
handler.patch(async (req, res) => {
if (!req.user) {
req.status(401).end();
return;
}
const { name, Bio } = req.body;
await req.db.collection('users').updateOne(
{ _id: req.user._id },
{
$set: {
name:name,
Bio: Bio,
},
},
);
res.json({ user: { name, Bio } });
});
export default handler;
I have encountered a this issue.
I was resolve it by use 2 form, a form use to get user's info as email, password and the other for send user's picture.
Maybe has best practice for this case.

Resources