multer gives me error "originalname" of undefined - reactjs

I'm trying to make an image uploader but I'm getting this error whenever I try to upload an image. i think all the configurations I've made with multer are alright so I can't seem to find where is the error.
I'm doing this with the multer module. If anyone can help me I would really appreciate it as I've been trying to solve this error for quite a while and I keep getting the same error.
server side:
const express = require('express');
const router = express.Router();
const multer = require('multer');
const Clients = require('../models/clients.js')
const storage = multer.diskStorage({
destination: (req, file, callback) => {
callback(null, "./uploads")
},
filename: (req, file, callback) => {
callback(null, file.originalname)
}
})
const upload = multer({storage: storage})
router.route('/').get((req, res) => {
Clients.find()
.then(client => res.json(client))
.catch(err => res.status(400).json('Error:' + err))
})
router.route('/add', upload.single("articleimage")).post((req, res) => {
const newClient = new Clients({
image: req.file.originalname,
firstName: req.body.firstName,
lastName: req.body.lastName,
weight: req.body.weight,
BMI: req.body.BMI
})
newClient.save()
.then (() => res.json(newClient))
.catch(err => res.status(400).json('error' + err))
})
module.exports = router
Client side:
import React, {useState} from 'react'
import FileBase64 from 'react-file-base64';
import axios from 'axios'
function AddClient() {
const [firstName, setFirstName] = useState('')
const [lastName, setLastName] = useState('')
const [weight, setWeight] = useState('')
const [BMI, setBMI] = useState('')
const [image, setImage] = useState('')
const selectedFile = (e) => {
setImage(e.target.files[0])
}
console.log(image)
const submitForm = (e) => {
e.preventDefault()
const formData = new FormData()
formData.append("image", image)
formData.append("firstName", firstName)
formData.append("lastName", lastName)
formData.append("weight", weight)
formData.append("BMI", BMI)
axios.post('http://localhost:4000/clients/add', formData)
.then(res => console.log(res))
}
console.log(firstName)
return (
<div>
<form class="w-full max-w-lg" onSubmit={submitForm} encType="multipart/form-data">
<div class="flex flex-wrap -mx-3 mb-6">
<input type="file" filename="articleimage" onChange={selectedFile}/>
<div class="w-full md:w-1/2 px-3 mb-6 md:mb-0">
<label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-first-name">
First Name
</label>
<input class="appearance-none block w-full bg-gray-200 text-gray-700 border border-red-500 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white" id="grid-first-name" type="text" onChange={(e) => setFirstName(e.target.value)} placeholder="First Name" />
<p class="text-red-500 text-xs italic">Please fill out this field.</p>
</div>
<div class="w-full md:w-1/2 px-3">
<label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-last-name">
Last Name
</label>
<input class="appearance-none block w-full bg-gray-200 text-gray-700 border border-gray-200 rounded py-3 px-4 leading-tight focus:outline-none focus:bg-white focus:border-gray-500" id="grid-last-name" type="text" onChange={(e) => setLastName(e.target.value)} placeholder="Last Name" />
</div>
</div>
<div class="flex flex-wrap -mx-3 mb-6">
<div class="w-full md:w-1/2 px-3 mb-6 md:mb-0">
<label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-first-name">
Weight
</label>
<input class="appearance-none block w-full bg-gray-200 text-gray-700 border border-red-500 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white" id="grid-first-name" type="text" onChange={(e) => setWeight(e.target.value)} placeholder="Weight" />
<p class="text-red-500 text-xs italic">Please fill out this field.</p>
</div>
<div class="w-full md:w-1/2 px-3">
<label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-last-name">
Body Mass Index (BMI)
</label>
<input class="appearance-none block w-full bg-gray-200 text-gray-700 border border-gray-200 rounded py-3 px-4 leading-tight focus:outline-none focus:bg-white focus:border-gray-500" id="grid-last-name" type="text" onChange={(e) => setBMI(e.target.value)} placeholder="BMI" />
</div>
<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">Add Client</button>
</div>
</form>
</div>
)
}
export default AddClient

On the client-side, you are appending the formData as formData.append("image", image) but on the server-side, you are writing 'articleimage' in the upload middleware.
Change it to 'image' i.e. same as the client-side.
Also, I would suggest that the controller code for uploading/saving the image and saving the name of the image in the database should be separated.
You should create a new controller function that just saves the image on your server and returns the name of the image. Call this controller function through an API call in the selectedFile callback function. And then, save the returned name of the image in the state and finally proceed to submit the form and save the data in the database.

Related

I want to fetch the current value of text input field by using onChange event in react

The problem with the onChange event is, it is providing the value but with one character less.
Example : if I type "overflow" in the input box, the provided value will be "overflo" (without w).
It's not registering the last character input.
How can i get the full text from the input field. Is there any other way to solve this problem.
import React, {useState} from "react";
function Average() {
const [totalVal, setTotalVal] = useState("");
const [shares, setShares] = useState("");
const handleKeyDown = (event) => {
if (event.key === 'Enter') {
setTotalVal(totalVal + "+");
}
};
const calcAvg = (event) => {
let avg = eval(totalVal) / shares;
document.getElementById("avgBox").innerHTML = ": ₹" + avg.toFixed(2);
event.preventDefault();
};
return (
<>
<div>
<div className="my-4">
<label className="block">
<input
type="number"
id="totalShares"
className="mt-1 px-3 py-2 bg-white border shadow-sm border-slate-300 placeholder-slate-500 focus:outline-none focus:border-slate-300 focus:ring-slate-300 block w-full rounded-md sm:text-sm focus:ring-1"
placeholder="Enter number of stocks"
value={shares}
onChange={(event) => setShares(event.target.value)}
/>
</label>
</div>
<div className="my-4">
<label className="block">
<input
type="text"
id="stockVal"
value={totalVal}
className="mt-1 px-3 py-2 bg-white border shadow-sm border-slate-300 placeholder-slate-400 focus:outline-none focus:border-slate-300 focus:ring-slate-300 block w-full rounded-md sm:text-sm focus:ring-1"
placeholder="Enter price of every stock with + in between"
autoComplete="off"
onChange={(event) => {setTotalVal(event.target.value); calcAvg()}}
onKeyDown={handleKeyDown}
/>
</label>
</div>
<div
className="p-2 my-8 unselectable cursor-pointer flex w-full justify-center bg-green-400 hover:bg-green-500 text-white font-medium rounded"
onClick={calcAvg}>
Average Price<div id="avgBox"></div>
</div>
</div>
</>
);
}
export default Average;
Here is an example of getting input values with useRef() hook.
const ref = useRef();
const handleChange = () => {
console.log(ref.current.value);
};
return (
<div className="App">
<h2>I'm trying to get input value</h2>
<input
type="text"
ref={ref}
onChange={handleChange}
placeholder="type something..."
/>
</div>
);
CodeSandbox:
https://codesandbox.io/s/kind-framework-ge6w2e?file=/src/App.js
The problem is that you setTotalVal and immediately call calcAvg. Since setState is sort of async this function won't have the updated value until the next render.
You can fix this by using another state in combination with a useEffect. We'll create averageValue state to store the average value. Then we add the useEffect to run whenever the totalVal or the shares change. Instead of the document.getElementBy we can simply display the average value the React way.
I removed the calcAvg since we don't need it anymore when using the new functionality.
Note: It is not recommended to manipulate the DOM using document.getElementBy....
import React, { useState, useEffect } from "react";
function Average() {
const [totalVal, setTotalVal] = useState("");
const [shares, setShares] = useState("");
const [averageValue, setAverageValue] = useState(0);
const handleKeyDown = (event) => {
if (event.key === "Enter") {
setTotalVal(totalVal + "+");
}
};
useEffect(() => {
const avg = eval(totalVal) / shares;
setAverageValue(avg);
}, [totalVal, shares]);
return (
<>
<div>
<div className="my-4">
<label className="block">
<input
type="number"
id="totalShares"
className="mt-1 px-3 py-2 bg-white border shadow-sm border-slate-300 placeholder-slate-500 focus:outline-none focus:border-slate-300 focus:ring-slate-300 block w-full rounded-md sm:text-sm focus:ring-1"
placeholder="Enter number of stocks"
value={shares}
onChange={(event) => setShares(event.target.value)}
/>
</label>
</div>
<div className="my-4">
<label className="block">
<input
type="text"
id="stockVal"
value={totalVal}
className="mt-1 px-3 py-2 bg-white border shadow-sm border-slate-300 placeholder-slate-400 focus:outline-none focus:border-slate-300 focus:ring-slate-300 block w-full rounded-md sm:text-sm focus:ring-1"
placeholder="Enter price of every stock with + in between"
autoComplete="off"
onChange={(event) => {
setTotalVal(event.target.value);
}}
onKeyDown={handleKeyDown}
/>
</label>
</div>
<div className="p-2 my-8 unselectable cursor-pointer flex w-full justify-center bg-green-400 hover:bg-green-500 text-white font-medium rounded">
Average Price: ₹ {averageValue.toString()}
</div>
</div>
</>
);
}
Side note: Although eval works, I suggest you try and take a look into storing your stock prices and amounts within some kind of array. This way you can, in the future, for example list all of you stocks.

fetched data is not displayed in React

Iam trying to fetch the data from this API
https://covid-19-coronavirus-statistics.p.rapidapi.com/v1/total
the data is successfully displayed in the console
but it is not displayed in the browser
const [selectedCountry, setSelectedCountry] = useState('USA');
const [stats, setStats] = useState({});
useEffect(() => {
const fetchData = async () => {
const result = await axios.get(API_URL, {
headers: {
'x-rapidapi-host': 'covid-19-coronavirus-statistics.p.rapidapi.com',
'x-rapidapi-key': 'YOUR_API_KEY',
},
params: {
country: selectedCountry
}
});
setStats(result.data.data);
};
fetchData();
}, [selectedCountry]);
return (
<div className="flex flex-col items-center justify-center p-4">
<div className="select w-full md:w-1/2">
<h2 className="text-xl text-red-600 font-medium mb-3">Select a Country:</h2>
<select value={selectedCountry} onChange={e => setSelectedCountry(e.target.value)} className="block appearance-none w-full bg-white text-blue-500 border border-gray-400 hover:border-gray-500 px-4 py-2 pr-8 rounded shadow leading-tight focus:outline-none focus:shadow-outline">
{countries.map(country => (
<option key={country} value={country}>{country}</option>
))}
</select>
</div>
<div className="stats w-full text-blue-600 md:w-1/2 mt-4">
<h2 className="text-xl text-red-600 font-medium mb-3">Selected Country:</h2>
<h2 className='text-yellow-600 mb-4 bg-gray-200'>{selectedCountry}</h2>
<p className="stat">Confirmed Cases: {stats.confirmed}</p>
<p className="stat">Deaths: {stats.deaths}</p>
<p className="stat">Recoveries: {stats.recovered}</p>
</div>
</div>
the result in the console is:
data : confirmed : 4563190 deaths : 50541 lastChecked : "2023-01-31T18:11:16+00:00" lastReported : "2023-01-31T04:20:32+00:00" location : "US" recovered : null
also i picture
Note that ive already used my own API key

Black screen in react code - possible firebase auth issue

this code is giving me a blank screen i have no idea as to why i would assume its firebase auth or the logic giving me trouble im running this in vscode and its compiling sucessfully
import { createUserWithEmailAndPassword } from "firebase/auth";
import React, { useState } from "react";
import { auth } from "../firebase";
import { Link } from "react-router-dom";
const SignUp = () => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [name, setName] = useState("");
const [error, setError] = useState("");
const signUp = (e) => {
e.preventDefault();
auth.createUserWithEmailAndPassword(email, password).then((userCredential) => {
userCredential.user
.updateProfile({
displayName: name,
})
.then(() => {
console.log("user name added");
})
.catch((error) => {
console.log(error);
});
})
.catch((error) => {
setError(error.message);
});
};
return (
<div className="container mx-auto flex justify-center pt-6">
<form className="bg-white p-6 rounded-lg shadow-xl" onSubmit={signUp}>
<div className="mb-4">
<label
className="block text-gray-700 font-medium mb-2"
htmlFor="name"
>
Name
</label>
<input
className="w-full border border-gray-400 p-2 rounded-md"
id="name"
type="text"
placeholder="Enter your name"
onChange={(e) => setName(e.target.value)}
/>
</div>
<div className="mb-4">
<label
className="block text-gray-700 font-medium mb-2"
htmlFor="email"
>
Email
</label>
<input
className="w-full border border-gray-400 p-2 rounded-md"
id="email"
type="email"
placeholder="Enter your email"
onChange={(e) => setEmail(e.target.value)}
/>
</div>
<div className="mb-4">
<label
className="block text-gray-700 font-medium mb-2"
htmlFor="password"
>
Password
</label>
<input
className="w-full border border-gray-400 p-2 rounded-md"
id="password"
type="password"
placeholder="Enter your password"
onChange={(e) => setPassword(e.target.value)}
/>
</div>
<div className="text-red-500 text-xs mb-4">{error}</div>
<button
className="bg-indigo-500 hover:bg-indigo-600 text-white py-2 px-4
rounded-md"
type="submit"
>
Sign Up
</button>
<Link
to="/login"
className="text-indigo-500 hover:text-indigo-600 font-medium py-2 px-4"
>
Already have an account?
</Link>
</form>
</div>
);
};
export default SignUp;
was trying to make a signup page for my website that took in email and password to authenticate but also took a name to display on profile

Yup custom validation not working in Typescript

I need to validate object when form submit. Because of field value is object, I wrote isObjectEmpty method and added into yup. But I got following error. I am using React + Typescript. May I know what I am missing here?
Property 'isObjectEmpty' does not exist on type 'MixedSchema<any,
AnyObject, any>'.ts(2339)
import AutoComplete from "./AutoComplete";
import DatePicker from "react-datepicker";
import { Formik, Form, ErrorMessage } from "formik";
import * as Yup from "yup";
import { SearchCriteria } from "../types";
import { useAppDispatch, useAppSelector } from "../store/hooks";
import { fetchJourneys } from "../reducers/journeySlice";
import { useNavigate } from "react-router-dom";
import { Stop } from "../types";
import { useEffect } from "react";
const isObjectEmpty = (message: string) =>{
return Yup.mixed().test("isObjectEmpty", message, function (value) {
const { path, createError } = this;
if (value === null) {
return createError({ path, message: message ?? "Field is required." });
}
return true;
});
}
Yup.addMethod(Yup.mixed, "isObjectEmpty", isObjectEmpty);
const schema = Yup.object({
from: Yup.mixed().isObjectEmpty("To is required."),
to: Yup.mixed().isObjectEmpty("To is required."),
departure: Yup.date().required("Date is required"),
});
interface FormData {
from: Stop | null;
to: Stop | null;
departure: Date;
}
const initialValues: FormData = { from: null, to: null, departure: new Date() };
export const SearchForm = () => {
const journey = useAppSelector((state) => state.journey);
const navigate = useNavigate();
const dispatch = useAppDispatch();
useEffect(() => {
if (journey.criteria) {
initialValues.from = journey.criteria.from;
initialValues.to = journey.criteria.to;
initialValues.departure = journey.criteria.departure;
}
}, []);
const handleSearch = async (criteria: SearchCriteria) => {
console.log(criteria);
await dispatch(fetchJourneys(criteria));
// redirect to result page
navigate("/journey");
};
return (
<Formik
initialValues={initialValues}
validationSchema={schema}
onSubmit={handleSearch}
>
{({
values,
errors,
touched,
handleChange,
handleBlur,
handleSubmit,
isSubmitting,
setFieldValue,
/* and other goodies */
}) => (
<Form>
<div className="grid gap-6 mb-6 md:grid-cols-2">
<div>
<label
htmlFor="first_name"
className="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
>
From
</label>
<AutoComplete
style="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="Station / stop / address"
name="from"
value={values.from}
valueChangeHandler={(from: Stop) => setFieldValue("from", from)}
/>
<ErrorMessage
component="span"
name="from"
className=" text-xs text-red-400"
/>
</div>
<div>
<label
htmlFor="last_name"
className="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
>
To
</label>
<AutoComplete
style="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="Station / stop / address"
name="to"
value={values.to}
valueChangeHandler={(to: Stop) => setFieldValue("to", to)}
/>
<ErrorMessage
component="span"
name="to"
className=" text-xs text-red-400"
/>
</div>
<div>
<label
htmlFor="company"
className="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
>
Outbound journey
</label>
<DatePicker
className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
selected={values.departure}
onChange={(date) => setFieldValue("departure", date)}
timeInputLabel="Time:"
dateFormat="MM/dd/yyyy h:mm aa"
showTimeInput
name="departure"
/>
<ErrorMessage
component="span"
name="departure"
className=" text-xs text-red-400"
/>
</div>
</div>
<div className="flex justify-end">
<button
type="submit"
className="btn btn-primary text-black hover:text-white focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm w-full sm:w-auto px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
>
Search
</button>
</div>
</Form>
)}
</Formik>
);
};

How to upload file to Django rest framework API using Axios and react hook form?

I have created an API using Django Rest Framework. It has an image uploading option. But I can not upload the file. I am using Axios for API calls and react hook form for form handling. I am posting the code below for better understanding.
Django:
Model:
class BlogModel(models.Model):
user = models.ForeignKey(user_model.User, on_delete=models.CASCADE, related_name="user_blog")
blogtitle = models.CharField(max_length=250)
blogcontent = models.TextField()
blogimg = models.ImageField(upload_to="blog_image", blank=True)
slug = models.SlugField(max_length=250, unique=True)
tags = models.ManyToManyField(BlogTagsModel, related_name='blog_tags', blank=True, null=True)
published_date = models.DateTimeField(auto_now_add=True)
edit_date = models.DateTimeField(auto_now=True)
Serializer
class BlogSerializer(serializers.ModelSerializer):
class Meta:
model = blog_model.BlogModel
fields = '__all__'
extra_kwargs = {
'user': {'read_only': True},
'slug': {'read_only': True},
}
View
class BlogPostView(generics.ListCreateAPIView):
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
serializer_class = blog_ser.BlogSerializer
queryset = blog_model.BlogModel.objects.all()
def perform_create(self, serializer):
rand_num = random.randint(99, 222)
blog_slug_str = f"{serializer.validated_data.get('blogtitle')}{rand_num}"
sample_string_bytes = blog_slug_str.encode("ascii")
base64_bytes = base64.b64encode(sample_string_bytes)
slug = base64_bytes.decode("ascii")
serializer.save(user=self.request.user, slug=slug)
React:
Form JSX
<form className="bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4" onSubmit={handleSubmit(onSubmit)}>
<div className="mb-4">
<label
className="block text-gray-700 text-sm font-bold mb-2"
htmlFor="title"
>
Title
</label>
<input
className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
id="title"
type="text"
{...register('title', { required: true })}
/>
{errors.title && <p className="text-red-500 text-xs italic">Title is required</p>}
</div>
<div className="mb-4">
<label
className="block text-gray-700 text-sm font-bold mb-2"
htmlFor="image"
>
Image
</label>
<input
className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
id="image"
type="file"
{...register('image', { required: true })}
/>
{errors.image && <p className="text-red-500 text-xs italic">Image is required</p>}
</div>
<div className="mb-4">
<label
className="block text-gray-700 text-sm font-bold mb-2"
htmlFor="details"
>
Details
</label>
<textarea
className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
id="details"
{...register('details', { required: true })}
/>
{errors.details && <p className="text-red-500 text-xs italic">Details is required</p>}
</div>
<button
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
type="submit"
>
Submit
</button>
</form>
Axios Call
const onSubmit = data => {
console.log(data.image['0']);
const payload = {
blogtitle: data.title,
blogcontent: data.details,
blogimg: data.image,
}
console.log(payload);
myAxios.post('/api/post/', payload).then(res => {
console.log(res);
}).catch(err => {
console.log(err.response.data);
})
}
When I am submitting the form the error is saying, The submitted data was not a file. Check the encoding type on the form..
When consoling the payload, I am getting:
{
"blogtitle": "nok",
"blogcontent": "asasa",
"blogimg": {
"0": {}
}
}
Please Help Me...
I have found the solution.
Turns out I have to add a header 'Content-Type': 'multipart/form-data' to the Axios Request.
I am posting the updated code below:
Axios Call
const onSubmit = data => {
const payload = {
blogtitle: data.title,
blogcontent: data.details,
blogimg: data.image['0'],
}
console.log(payload);
myAxios.post('/api/post/', payload, {
headers: {
'Content-Type': 'multipart/form-data'
}
}).then(res => {
console.log(res);
}).catch(err => {
console.log(err.response.data);
})
}

Resources