api proplem and redux - node.js

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>

Related

React - Display product in Cart one row per id/quantity

I've got a React App, that scans products via a Scanner(using serialport.io + socket.io) and adds each scanned product into my frontend cart component.
Right now I got it working, but my solution creates a new row in my cart per product scanned as you can see here and I need it to display a new row only the first time a prod is scanned and then if the same product is detected it only updates the quantity and total per product and also the cart total, something like this...
From what I've searched the best way to do this would be by using react useContext and useReducer but I can't get it working.
This is my code on server side index.js:
io.on("connection", (socket) => {
console.log("Socket Connected");
const port = new SerialPort({
path: "COM6",
baudRate: 9600,
autoOpen: false,
});
socket.on("start_scanner", () => {
port.open(function (err) {
if (err) {
console.log("Error opening port: ", err.message);
}
else{
console.log("Scanner Connected");
}
});
port.on("open", function () {
setInterval(function () {
const portReader = port.read();
if (portReader != null) {
const sensorVal = Buffer.from(portReader).toString();
const soap = require("soap");
const url = "http://example.com?wsdl";
soap.createClient(url, function (err, client) {
client.GetProductById(
{
UserId: "1",
ProductId: sensorVal,
},
function (err, result) {
if (err) return console.log(err);
let productScanned = result.GetProductByIdResult;
socket.broadcast.emit("add_product_to_list", productScanned);
}
);
});
}
}, 700);
});
});
This is my Cart component code:
import { useState, useEffect } from "react";
import io from "socket.io-client";
import ProductRow from "./ProductRow";
import "./ProductsList.css";
const socket = io.connect("http://localhost:5000");
const ProductsList = (props) => {
const [scannedData, setScannedData] = useState([]);
useEffect(() => {
socket.on("add_product_to_list", (productScanned) => {
setScannedData((prevProducts) => [...prevProducts, productScanned]);
});
}, [socket]);
return (
<div className="w-9/12 h-full px-20 py-20 flex flex-col ">
<div className="w-full h-auto my-2 px-3 py-3 font-semibold grid grid-cols-3 bg-blue-600 rounded-xl">
<div className="w-[60%] text-left">Product</div>
<div className="w-[20%] text-left">Quant.</div>
<div className="w-[20%] text-left">Price</div>
</div>
<div
id="products-wrapper"
className="w-full h-[95%] flex flex-col overflow-x-hidden overflow-y-scroll"
>
{scannedData.map((productScanned) => (
<ProductRow data={productScanned} />
))}
</div>
<div className="w-full h-[15%] flex flex-row justify-end">
<div className="w-[20%] h-auto px-3 py-3 font-semibold flex flex-col justify-center bg-blue-600 rounded-xl ">
<div className="w-full text-left">
Total: {/* Total price amount */}{" "}
</div>
<div className="w-full text-left">
Qty: {0}
</div>
</div>
</div>
</div>
);
};
export default ProductsList;
This is my Cart Row component:
import "./ProductsList.css";
const ProductRow = ({ data }) => {
return (
<div
className="product-row w-full h-auto my-2 px-3 py-3 text-black text-center grid grid-cols-3 rounded-xl "
key={data._x003C_Id_x003E_k__BackingField}
>
<div className="w-[60%] text-left">
{data._x003C_Name_x003E_k__BackingField}
</div>
<div className="w-[20%] text-left">{1}</div>
<div className="w-[20%] text-left">
{parseFloat(data._x003C_Price_x003E_k__BackingField).toFixed(2)}€
</div>
</div>
);
};
export default ProductRow;
I've also got a cart-context.js and a CartProvider.js files which I was using to achieve my goal but can't get it to work.
/*cart-context.js*/
import React from "react";
const CartContext = React.createContext({
items: [],
totalAmount: 0,
addItem: (item) => {},
removeItem: (id) => {},
});
export default CartContext;
/*CartProvider.js*/
import { useContext, useReducer } from "react";
import CartContext from "./cart-context";
const defaultCartState = {
items: [],
totalAmount: 0,
};
const cartReducer = (state, action) => {
if (action.type === "ADD_ITEM") {
const updatedTotalAmount = state.totalAmount + action.item.price * action.item.amount;
const existingCartItemIndex = state.items.findIndex(
(item) => item.id === action.item.id
)
const existingCartItem = state.items[existingCartItemIndex];
let updatedItems;
if(existingCartItem){
const updatedItem = {
...existingCartItem,
amount: existingCartItem.amount + action.item.amount
}
updatedItems = [...state.items];
updatedItems[existingCartItemIndex] = updatedItem;
} else{
updatedItems = state.items.concat(action.item);
}
return {
items: updatedItems,
totalAmonut: updatedTotalAmount,
};
}
return defaultCartState;
};
const CartProvider = (props) => {
const [cartState, dispatchCartAction] = useReducer(
cartReducer,
defaultCartState
);
const addItemToCartHandler = (item) => {
dispatchCartAction({ type: "ADD_ITEM", item: item });
};
const removeItemFromCartHandler = (id) => {
dispatchCartAction({ type: "REMOVE_ITEM", id: id });
};
const cartContext = {
items: cartState.items,
totalAmonut: cartState.totalAmonut,
addItem: addItemToCartHandler,
removeItem: removeItemFromCartHandler,
};
return (
<CartContext.Provider value={cartContext}>
{props.children}
</CartContext.Provider>
);
};
export default CartProvider;
Could anyone help me out and help me understand my mistakes?
Thanks in advance.

Warning: Each child in a list should have a unique "key" prop and does not save in my json-server

I keep getting error "Warning: Each child in a list should have a unique "key" prop" on console log. I think there is something wrong in the
axios.delete('http://localhost:3000/contacts/${id}
' or
axios.put('http://localhost:3000/contacts/${isUpdate.id}'
It works, but does not configure in the json-server that I made. So the changes does not happen in the json-server.
The "save" button works, and added new data into my json-server. But, when I tried to edit, or delete, it does not save the changed in json-server.
My code:
import "./App.css";
import List from "./List";
import { useState,useEffect } from "react";
import {uid} from "uid";
import axios from "axios";
function App() {
const [contacts, setContacts] = useState([]);
function handleChange(e) {
let data = { ...formData };
data[e.target.name] = e.target.value;
setFormData(data);
}
const [isUpdate, setIsUpdate] = useState({ id: null, status: false});
const [formData, setFormData] = useState({
name: "",
telp: "",
});
useEffect(() => {
axios.get("http://localhost:3000/contacts").then((res) => {
console.log(res.data);
setContacts(res?.data ?? []);
});
}, []);
function handleSubmit(e){
e.preventDefault();
alert("Oke Bos!");
let data = [...contacts];
if(formData.name === "") {
return false;
}
if(formData.telp === "") {
return false;
}
if ( isUpdate.status){
data.forEach((contact) => {
if (contact.id === isUpdate.id) {
contact.name = formData.name;
contact.telp = formData.telp;
}
});
axios.put('http://localhost:3000/contacts/${isUpdate.id}', {
name: formData.name,
telp: formData.telp,
})
.then((res) => {
alert("Berhasil edit data!");
});
} else {
let newData = { id: uid(), name: formData.name, telp: formData.telp };
data.push(newData);
axios.post("http://localhost:3000/contacts", newData).then((res) => {
alert("Data telah disimpan cok!");
});
}
// tambah kontak yee
setIsUpdate({id: null, status: false});
setContacts(data);
setFormData({ name: "", telp: ""});
}
function handleEdit(id) {
let data = [...contacts];
let foundData = data.find((contact) => contact.id === id);
setFormData({ name: foundData.name, telp: foundData.telp});
setIsUpdate({id: id, status: true});
}
function handleDelete(id) {
let data = [...contacts];
let filteredData = data.filter((contact) => contact.id !== id);
axios.delete('http://localhost:3000/contacts/${id}').then((res) => {
alert("Data telah dihapus");
});
setContacts(filteredData);
}
return (
<div className="App">
<h1 className="px-3 py-3">My Contact List</h1>
<form onSubmit={handleSubmit} className="px-3 py-4">
<div className="form-group">
<label htmlFor="">Name</label>
<input type="text"
className="form-control"
onChange={handleChange}
value={formData.name}
name="name" />
</div>
<div className="form-group mt-3">
<label htmlFor="">No. Telp</label>
<input type="text"
className="form-control"
onChange={handleChange}
value={formData.telp}
name="telp" />
</div>
<div>
<button type="submit" className="btn btn-primary w-100 mt-3">
Save
</button>
</div>
</form>
<List handleDelete={handleDelete} handleEdit={handleEdit} data={contacts} />
</div>
);
}
export default App;
The list component:
import React from "react";
export default function List({data,handleEdit,handleDelete}) {
return (
<div className="list-group">
{
data.map((contact) => {
return (
<div className="list-group-item list-group-item-action">
<div className="d-flex w-100 justify-content-between">
<h5 className="mb-1">{contact.name}</h5>
<div>
<button onClick={() => handleEdit(contact.id)} className="btn btn-sm btn-link">Edit</button>
<button onClick={() => handleDelete(contact.id)} className="btn btn-sm btn-link">Del</button>
</div>
</div>
<p className="mb-1">{contact.telp}</p>
</div>
);
})}
</div>
);
}
I follow the tutorial, the code is like this, i checked it in so many times. But still it does not configure well into json-server like what i see on youtube.

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

Why did it failed to load? the server responded with a status of 404 (Not Found)

I have my app(express+Next.js.MOngoDB).I signuped all my users,works Ok. Now when I try signin
http://localhost:5000/signin
I got error
Uncaught (in promise) Error: Request failed with status code 404
at createError (webpack-internal:///../../node_modules/axios/lib/core/createError.js:16)
at settle (webpack-internal:///../../node_modules/axios/lib/core/settle.js:17)
Terminal
GET /__nextjs_original-stack-frame?isServerSide=false&file=webpack-internal%3A%2F%2F%2F..%2F..%2
POSTMAN signin works fine
app.js(just API)
const app = express();
app.use('/api/v1/auth', auth);
Next.js,Signin
import SigninComponent from '../components/frontauth/SigninComponent';
const Signin = () => {
return (
<Layout>
<h2 className="text-center pt-3 pb-4">Signin</h2>
<div className="row">
<div className="col-md-6 offset-md-3">
<SigninComponent />
</div>
</div>
</Layout>
);
};
SigninComponent
/* eslint-disable no-undef */
import axios from 'axios';
import React, { useState } from 'react';
import { API } from '../../config';
const SigninComponent = () => {
const [values, setValues] = useState({
email: '',
password: ''
});
const [loading, setLoading] = useState(false);
const { email, password } = values;
const handleSubmit = async (e) => {
e.preventDefault();
const { email, password } = values;
const user = { email, password};
await axios.post(`${API}/api/v1/auth/signin`, user);
};
const handleChange = name => e => {
setValues({ ...values, error: false, [name]: e.target.value });
};
const showLoading = () => (loading ? <div className="alert alert-info">Loading...</div> : '');
const signinForm = () => {
return (
<form onSubmit={handleSubmit}>
<div className="form-group">
<input
value={values.email}
onChange={handleChange('email')}
type="email"
className="form-control"
placeholder="Type your email"
/>
</div>
<div className="form-group">
<input
value={values.password}
onChange={handleChange('password')}
type="password"
className="form-control"
placeholder="Type your password"
/>
</div>
<div>
<button className="btn btn-primary">Signin</button>
</div>
</form>
);
};
return <React.Fragment>
{showLoading()}
{signinForm()}
</React.Fragment>;
};
export default SigninComponent;
How should I edit .babelrc,I have only one line,should I add plugins?
"presets": ["next/babel"]
Why does the terminal point to SSR FALSE?

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