It's extremely weird that all the tutorials I've found online show how to upload an image, but do not show how to do it with other text inputs included.
Thus, I've hit a roadblock trying to upload images, as well as other textual data in the same form. Spent hours searching on SO and Google, but couldn't find anything that fit my situation.
I'm using React & Redux, and the express-fileupload package for file uploads.
Anyway, here's what I've tried:
Backend
campgroundRoutes.js
const express = require('express');
const router = express.Router();
const fileUpload = require('express-fileupload');
router.use(fileUpload());
const Campground = require(`../../models/campground`);
const checkAuth = require('../../middleware/check-auth');
router.post('/', checkAuth, (req, res, next) => {
const file = req.files.file;
console.log('req.files: ', req.files); // req.files is undefined
uploadPath = './assets/uploadedImages/' + file.name;
file.mv(uploadPath, err => {
if (err) {
console.error('Error: ', err);
return res
.status(500)
.json({ error: err, message: 'Failed to upload file' });
}
res.json({ fileName: file.name });
});
const campgroundToPost = new Campground({
title: req.body.title,
description: req.body.description,
cost: req.body.cost,
imageName: file.name,
_author: {
id: req.userData.userId,
firstName: req.userData.firstName
}
});
campgroundToPost
.save()
.then(result => res.status(200).json({ campground: result }))
.catch(err => res.status(400).send(`Failed to add campground, ${err}`));
});
Frontend
addCampground.js
import React, { Component } from 'react';
import TextField from '#material-ui/core/TextField';
import Card from '#material-ui/core/Card';
import CardContent from '#material-ui/core/CardContent';
import Typography from '#material-ui/core/Typography';
import Button from '#material-ui/core/Button';
import '../../styles/addCampground.css';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import {
actionAddCampground,
getCampgroundDetails
} from './actions/campgroundActions';
class AddCampground extends Component {
constructor(props) {
super(props);
this.state = {
title: '',
description: '',
cost: '',
selectedImage: null
};
}
handleChange = e => {
const { name, value } = e.target;
this.setState({
[name]: value
});
};
uploadImage = e => {
this.setState(
{
selectedImage: e.target.files[0]
},
() => console.log(this.state.selectedImage)
);
};
addCampground = () => {
const title = this.state.title;
const description = this.state.description;
const cost = this.state.cost;
const data = new FormData();
data.append('file', this.state.selectedImage);
this.props
.actionAddCampground({
title,
description,
cost,
data
})
.then(res => console.log(res))
.catch(err => console.log('Error: ', err));
this.props.history.push('/home');
};
render() {
return (
<Card className="add-campground-card">
<CardContent>
<Typography
style={{ fontWeight: '400' }}
className="text-center"
variant="h6"
component="h6">
Add Your Campground
</Typography>
</CardContent>
<TextField
autoComplete="off"
name="title"
className="textfield"
label="Campground name"
variant="outlined"
value={this.state.title}
onChange={e => this.handleChange(e)}
/>
<TextField
autoComplete="off"
name="description"
className="textfield"
label="Campground description"
variant="outlined"
value={this.state.description}
onChange={e => this.handleChange(e)}
/>
<TextField
autoComplete="off"
name="cost"
className="textfield"
type="number"
label="Campground cost"
variant="outlined"
value={this.state.cost}
onChange={e => this.handleChange(e)}
/>
<input onChange={this.uploadImage} type="file" name="file" />
<Button
className="add-campground"
variant="contained"
color="primary"
onClick={this.addCampground}>
Add Campground
</Button>
</Card>
);
}
}
const mapStateToProps = state => {
return {
campground: state.campgroundList.singleCampground
};
};
const mapDispatchToProps = dispatch => {
return bindActionCreators(
{
actionAddCampground,
getCampgroundDetails
},
dispatch
);
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(AddCampground);
campgroundActions.js
// Only including add campground action here as only that's relevant
import {
ADD_CAMPGROUND,
} from '../actionTypes/types';
import axios from 'axios';
import { authHeader } from '../../../helpers/auth-header';
const API_URL = `http://localhost:5000/api`;
export const actionAddCampground = campground => {
return async dispatch => {
try {
const res = await axios.post(`${API_URL}/campgrounds`, campground, {
headers: authHeader()
});
dispatch({
type: ADD_CAMPGROUND,
payload: res
});
return res.data;
} catch (err) {
return console.log(err);
}
};
};
authHeader.js
export const authHeader = () => {
let user = JSON.parse(localStorage.getItem('user'));
let token = localStorage.getItem('token');
if (user && token) {
return {
Authorization: token
};
} else {
return {};
}
};
This is error I get in the backend:
TypeError: Cannot read property 'file' of undefined
I cannot figure out where I'm going wrong.
A few days back Even I was facing the same problem where I have to send the file and another Input field Eventually this below code helped me while making an API call see if it is any help to you
//-------------To insert Documents and InputField--------------//
export function documentsInsert( documentName, document ) {
var formData = new FormData();
formData.append("documentName", documentName)
formData.append("document", document)
return request2({
url: xyzzz,
method: 'POST',
body: formData
});
}
Related
i am working on a small application for learning purposes i am trying to access a bunch of data sent to the backend using formdata in react. i am trying to access the data on the backend using req.fields and i keep getting undefined
React code
import { useState } from 'react';
import axios from "axios";
import TextField from '#mui/material/TextField';
import { DatePicker } from "antd";
import moment from "moment";
const CreateHotels = () => {
const user = localStorage.getItem("auth");
let userObj = JSON.parse(user);
const token = userObj.token;
const [values, setValues] = useState({
title: "",
bed: "",
from: "",
to: "",
image: "",
price: ""
})
const { image, title, bed, from, to, price } = values;
const handleSubmit = async (e) => {
e.preventDefault();
let hotelData = new FormData();
hotelData.append("title", title)
hotelData.append("bed", bed)
hotelData.append("from", from)
hotelData.append("to", to)
image && hotelData.append("image", image)
hotelData.append("price", price)
try {
let response = await axios.post(`http://localhost:7000/api/createhotels`, hotelData , {
headers: {
Authorization: `Bearer ${token}`
}
})
console.log(response)
} catch (err) {
console.log(err)
}
}
const handleImageChange = (e) => {
setValues((prevValues) => ({ ...prevValues, image: e.target.files[0] }))
}
const handleChange = (e) => {
const { target: { value, name } } = e
setValues(prevValues => ({ ...prevValues, [name]: value }))
}
return (
<div>
<form onSubmit={handleSubmit}>
<input onChange={handleImageChange} type="file" />
<TextField name="title" onChange={handleChange} type="text" className='fields' id="outlined-basic" label="Title" variant="outlined" />
<TextField name="bed" onChange={handleChange} type="text" className='fields' id="outlined-basic" label="Bed" variant="outlined" />
<DatePicker
name="from"
className="datepickers"
onChange={(date, dateString) => setValues(prevValues => ({...prevValues, from: dateString}))}
disabledDate={(current) => current.valueOf() < moment().subtract(1, "days")}
/>
<DatePicker
name='to'
className="datepickers"
onChange={(date, dateString) => setValues(prevValues => ({...prevValues, to: dateString}))}
disabledDate={(current) => current.valueOf() < moment().subtract(1, "days")}
/>
<TextField name='price' onChange={handleChange} className='fields' id="outlined-basic" label="Price" variant="outlined" />
<button>POST HOTEL</button>
</form>
</div>
)
}
export default CreateHotels;
express controller
const Hotel = require("../models/hotel");
const fs = require('fs');
const createhotels = async (req, res) => {
try {
let fields = req.fields;
let files = req.files;
console.log(fields)
} catch (err) {
console.log(err)
}
}
module.exports = {
createhotels
}
express routes
const express = require("express");
const router = express.Router();
const { createhotels } = require("../controllers/hotel");
router.post("/createhotels", createhotels);
module.exports = router;
I have tried using req.fields to access the data but i keep getting undefined. Please i will appreciate if anyone can help me
When receiving data from the server, the type is known, but only response.data does not work.
If I just take a response, it works fine, but when I take a response.data it doesn't work.
ErrorMessage : Object is of type 'unknown.'
import { UserState } from '../../lib/atom';
import React, { useEffect, useState } from 'react';
import { useRecoilState } from 'recoil';
import styled from 'styled-components';
import Resizer from 'react-image-file-resizer';
import CustomButton from '../../components/CustomButton';
import axios, { AxiosRequestHeaders, AxiosResponse } from 'axios';
import { getCookie } from '../../lib/cookie/cookie';
import { checkUsernameApi } from '../../apis/apiClient';
import useDebounce from '../../hooks/useDebounce';
const headers: AxiosRequestHeaders = {
Authorization: `Bearer ${getCookie('accessToken')}`,
};
function UserProfile() {
const [userState, setUserState] = useRecoilState(UserState);
const [imgText, setImgText] = useState('');
const [username, setUserName] = useState(userState.username);
const debounceUsername = useDebounce(username, 500);
const [exist, setExist] = useState(false);
function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
const revise_form = {
email: userState.email,
groupInfo: '39th',
profileImg: userState.profileImg,
username,
};
console.log('Revise_Form', revise_form);
const response = axios.put(
`http://localhost:8080/api/userprofile/${userState.username}`,
revise_form,
{ headers }
);
console.log('put response', response);
}
async function FetchingUserName() {
try {
const response = await checkUsernameApi(username);
return response;
} catch (err) {
return err;
}
}
useEffect(() => {
(async () => {
const response = await FetchingUserName();
console.log(response);
/* eslint-disable-next-line */
setExist(response.data);
})();
}, [debounceUsername]);
function fileChangedHandler(e: React.ChangeEvent<HTMLInputElement>) {
if (e.currentTarget.files !== null) {
const file = e.currentTarget.files[0];
setImgText(file.name);
Resizer.imageFileResizer(
file,
300,
300,
'JPEG',
100,
0,
(uri) => {
setUserState({ ...userState, profileImg: uri });
},
'base64',
200,
200
);
}
}
return (
<ProfileContainer>
<RevisionForm onSubmit={handleSubmit}>
<Title>UserProfile</Title>
<AvatarBox>
<Avatar src={userState.profileImg + ''} alt="avatar" />
</AvatarBox>
<FileBox>
<input
type={''}
placeholder="아바타를 업로드 해주세요."
disabled={true}
value={imgText}
/>
<label htmlFor="user_avatar">Upload</label>
<FileInput
id="user_avatar"
type="file"
onChange={fileChangedHandler}
/>
</FileBox>
<InputBox>
<label>email</label>
<InputEmail type={'text'} value={userState.email} disabled={true} />
</InputBox>
<InputBox>
<label>username</label>
<Input
type={'text'}
value={username}
onChange={(e) => {
setUserName(e.target.value);
}}
/>
</InputBox>
<CustomButton
height="4rem"
bgColor="#5de0e6"
color="white"
width="18rem"
weight="bold"
>
Revision
</CustomButton>
<ErrorText>{}</ErrorText>
</RevisionForm>
</ProfileContainer>
);
}
export default UserProfile;
I want to get response from server and save only response.data in setExist.
The response.data received from the server is a boolean type.
However, this does not work even if the type is specified in useState() .
Is there anything else I need to specify in this code?
enter image description here
I have tried the methods below.
useState<boolean>(false)
I put the Response type included in Axios.
useState<AxiosResponse>()
But the above methods didn't work.
My update function doesn't work when a trying to update some data. I receive this error
"Truncated incorrect DOUBLE value: 'undefined'"
I searched and some solutions said about the type of data being wrong, that a string value being compared with a varchar or int value, but the values that are passed are correct, my id column in database are receiving a int value.I saw that function CAST() could be used in cases where ocurred this type of error,but I dont know if is could be used in this case,I tried but doesn't worked.
This are my index.js code
const dotenv = require('dotenv')
const path = require('path');
const express = require('express')
const app = express();
const mysql = require('mysql2');
const cors = require('cors')
app.use(express.json());
app.use(cors());
dotenv.config({ path: path.join(__dirname, './.env')})
const db = mysql.createPool({
host: "localhost",
user: "root",
password: "root",
database: "phonecontact",
});
app.post("/register", (req,res) => {
const { name } = req.body;
const { phonenumber } = req.body;
const { email } = req.body;
let QUERY = "INSERT INTO contact ( name, phonenumber, email ) VALUES ( ?, ?, ? )";
db.query(QUERY, [name, phonenumber, email], (err,result) => {
if(err) console.log(err)
else res.send(result);
})
})
app.get("/getCards", (req,res) => {
let QUERY = "SELECT * FROM contact";
db.query(QUERY, (err,result) => {
if(err) console.log(err);
else res.send(result);
})
})
app.post('/search', (req,res) => {
const { name } = req.body;
const { email } = req.body;
const { phonenumber } = req.body;
let QUERY = "SELECT * FROM contact WHERE name = ? AND email = ? AND phonenumber = ?";
db.query(QUERY, [name,email,phonenumber], (err,result) => {
if(err) console.log(err)
else res.send(result)
})
})
app.put("/editContacts", (req,res) => {
const { id } = req.body;
const { name } = req.body;
const { email } = req.body;
const { phonenumber } = req.body;
let QUERY = "UPDATE contact SET name = ?, email = ?, phonenumber = ? WHERE idcontact = ?";
db.query(QUERY,[name,email,phonenumber,id], (err,result) => {
if(err) console.log(err);
else res.send(result)
})
})
app.delete("/delete/:id", (req,res) => {
const { id } = req.params;
let QUERY = "DELETE FROM contact WHERE idcontact = ?";
db.query(QUERY, id, (err,result) => {
if(err) console.log(err)
else res.send(result);
})
})
app.listen(3001, () => {
console.log('Servidor ligado na porta 3001')
})
This is my form that I'm using to insert data. I'm using material ui dialog form
import React, { useState } from "react";
import Button from '#mui/material/Button';
import TextField from '#mui/material/TextField';
import Dialog from '#mui/material/Dialog';
import DialogActions from '#mui/material/DialogActions';
import DialogContent from '#mui/material/DialogContent';
import DialogTitle from '#mui/material/DialogTitle';
import Axios from 'axios';
export default function FormDialog(props) {
const [editValues, setEditValues] = useState({
id: props.id,
name: props.name,
email: props.email,
phonenumber: props.phonenumber,
});
const handleChange = value => {
setEditValues((prevValues) => ({
...prevValues,
[value.target.id]: value.target.value,
}));
};
const handleClose = () => {
props.setOpen(false);
};
const handleEditContacts = () => {
Axios.put('http://localhost:3001/editContacts', {
id: editValues.id,
name: editValues.name,
email: editValues.email,
phonenumber: editValues.phonenumber,
})
handleClose();
}
const handleDeleteContacts = () => {
Axios.delete(`http://localhost:3001/delete/${editValues.id}`)
handleClose();
}
return (
<Dialog
open={props.open}
onClose={handleClose}
>
<DialogTitle>Editar</DialogTitle>
<DialogContent>
<TextField
autoFocus
margin="dense"
id="name"
label="Nome"
defaultValue={props.name}
onChange={handleChange}
type="text"
fullWidth
/>
<TextField
autoFocus
margin="dense"
id="email"
label="Email"
defaultValue={props.email}
onChange={handleChange}
type="email"
fullWidth
/>
<TextField
autoFocus
margin="dense"
id="telefone"
label="Telefone"
defaultValue={props.phonenumber}
onChange={handleChange}
type="tel"
fullWidth
/>
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>Cancelar</Button>
<Button onClick={handleEditContacts}>Salvar</Button>
<Button onClick={handleDeleteContacts}>Excluir</Button>
</DialogActions>
</Dialog>
);
}
My App.js
import React, { useEffect, useState } from 'react';
import './App.css';
import Axios from 'axios';
import Card from './Components/Card/Card';
function App() {
const [values, setValues] = useState();
const [listCard, setListCard] = useState([]);
const handleClick = () => {
Axios.post('http://localhost:3001/register', {
name: values.name,
phonenumber: values.phonenumber,
email: values.email,
}).then(() => {
Axios.post('http://localhost:3001/search', {
name: values.name,
phonenumber: values.phonenumber,
email: values.email,
}).then((response) => {
setListCard([
...listCard,
{
id: response.data[0].id,
name: values.name,
email: values.email,
phonenumber: values.phonenumber,
},
])
});
})
}
useEffect(() => {
Axios.get('http://localhost:3001/getCards').then((response) => {
setListCard(response.data);
})
}, [listCard]);
const handleChange = (value) => {
setValues((prevValues) => ({
...prevValues,
[value.target.name]: value.target.value,
}));
};
return (
<div className="App">
<div className="contact">
<h1>Cadastro de Contato</h1>
<input type="text" name="name" placeholder="Nome" className="register-input" onChange={handleChange} />
<input type="tel" name="phonenumber" placeholder="Telefone" className="register-input" onChange={handleChange} />
<input type="email" name="email" placeholder="Email" className="register-input" onChange={handleChange} />
<button onClick={handleClick}>Cadastrar</button>
</div>
{listCard.map((val) => (
<Card
key={val.idcontact}
listCard={listCard}
setListCard={listCard}
id={val.idcontact}
name={val.name}
email={val.email}
phonenumber={val.phonenumber}
/>
))}
</div>
);
}
export default App;
And finally my table structure
idcontact int AI PK
name varchar(100)
phonenumber varchar(11)
email varchar(100)
I'm following Amazon's Getting Started with AWS: https://aws.amazon.com/getting-started/hands-on/build-react-app-amplify-graphql/module-four/
I've built the app and have it hosted on git. Whenever I refresh the page or re-run "npm start", there are two entries in my notes app, even after I delete them.
https://i.stack.imgur.com/pRlLC.png
When I delete the notes and enter new 'notes' I get the error: "Each child in a list should have a unique "key" prop." and when I delete the note I get the error: "Uncaught (in promise)" DynamoDB:DynamoDbException
https://i.stack.imgur.com/2pMEG.png
Thanks for looking! The code in app.js is below:
import React, { useState, useEffect } from 'react';
import './App.css';
import { API, Storage } from 'aws-amplify';
import { withAuthenticator, AmplifySignOut } from '#aws-amplify/ui-react';
import { listNotes } from './graphql/queries';
import { createNote as createNoteMutation, deleteNote as deleteNoteMutation } from './graphql/mutations';
const initialFormState = { name: '', description: '' }
function App() {
const [notes, setNotes] = useState([]);
const [formData, setFormData] = useState(initialFormState);
useEffect(() => {
fetchNotes();
}, []);
async function onChange(e) {
if (!e.target.files[0]) return
const file = e.target.files[0];
setFormData({ ...formData, image: file.name });
await Storage.put(file.name, file);
fetchNotes();
}
async function fetchNotes() {
const apiData = await API.graphql({ query: listNotes });
const notesFromAPI = apiData.data.listNotes.items;
await Promise.all(notesFromAPI.map(async note => {
if (note.image) {
const image = await Storage.get(note.image);
note.image = image;
}
return note;
}))
setNotes(apiData.data.listNotes.items);
}
async function createNote() {
if (!formData.name || !formData.description) return;
await API.graphql({ query: createNoteMutation, variables: { input: formData } });
if (formData.image) {
const image = await Storage.get(formData.image);
formData.image = image;
}
setNotes([ ...notes, formData ]);
setFormData(initialFormState);
}
async function deleteNote({ id }) {
const newNotesArray = notes.filter(note => note.id !== id);
setNotes(newNotesArray);
await API.graphql({ query: deleteNoteMutation, variables: { input: { id } }});
}
return (
<div className="App">
<h1>My Notes App</h1>
<input
onChange={e => setFormData({ ...formData, 'name': e.target.value})}
placeholder="Note name"
value={formData.name}
/>
<input
onChange={e => setFormData({ ...formData, 'description': e.target.value})}
placeholder="Note description"
value={formData.description}
/>
<input
type="file"
onChange={onChange}
/>
<button onClick={createNote}>Create Note</button>
<div style={{marginBottom: 30}}>
{
notes.map(note => (
<div key={note.id || note.name}>
<h2>{note.name}</h2>
<p>{note.description}</p>
<button onClick={() => deleteNote(note)}>Delete note</button>
{
note.image && <img src={note.image} style={{width: 400}} />
}
</div>
))
}
</div>
<AmplifySignOut />
</div>
);
}
export default withAuthenticator(App);
Change
await API.graphql({ query: createNoteMutation, variables: { input: formData } });
to
const result = await API.graphql({ query: createNoteMutation, variables: { input: formData } });
Change the createNote and deleteNote functions to:
async function createNote() {
if (!formData.name || !formData.description) return;
const result = await API.graphql({ query: createNoteMutation, variables: { input: formData } });
if (formData.image) {
const image = await Storage.get(formData.image);
formData.image = image;
}
setNotes([ ...notes, {...result.data.createNote, ...formData} ]);
setFormData(initialFormState);
}
async function deleteNote( note ) {
const newNotesArray = notes.filter(n => n.id !== note.id);
await API.graphql({ query: deleteNoteMutation, variables: { input: { id:note.id } }});
setNotes(newNotesArray);
}
```
Please note that I've already checked answers in this question and nothing seems to work.
I'm using this repo as a boilerplate. Instead of firebase database, I'm trying to send username and email with the firebase auth userid , to the node server. I created an action creator signup to handle this.
This is signup.js action creator
import * as types from '../constants/action_types';
import axios from 'axios';
export const signup = (user) => {
console.log(user);
return async dispatch => {
try {
const response = await axios.get('http://localhost:5000/api/user/register', user)
const data = await {response};
dispatch({
type : types.SIGN_UP,
payload : data.fromback
})
} catch (error) {
console.lot(error)
}
}
}
Then I've connected it with the component with mapDispatchToProps., So,under the SignUpPage component, React dev tools shows signup as a function. But when it get triggers, it gives an error saying _this2.props.signup is not a function Why's that ?
This is my SignUpPage component
import React, { Component } from 'react';
import {
Link,
withRouter,
} from 'react-router-dom';
import { auth } from '../../firebase';
import * as routes from '../../constants/routes';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import {signup} from './../../actions/signup';
const SignUpPage = ({ history }) =>
<div>
<h1>SignUp</h1>
<SignUpForm history={history} />
</div>
const updateByPropertyName = (propertyName, value) => () => ({
[propertyName]: value,
});
const INITIAL_STATE = {
username: '',
email: '',
passwordOne: '',
passwordTwo: '',
error: null,
};
class SignUpForm extends Component {
constructor(props) {
super(props);
this.state = { ...INITIAL_STATE };
}
onSubmit = (event) => {
const {
username,
email,
passwordOne,
} = this.state;
const {
history,
} = this.props;
auth.doCreateUserWithEmailAndPassword(email, passwordOne)
.then(authUser => {
const userid = authUser.user.uid;
const user = { email, userid };
this.props.signup(user);
})
.catch(error => {
this.setState(updateByPropertyName('error', error));
});
event.preventDefault();
}
render() {
const {
username,
email,
passwordOne,
passwordTwo,
error,
} = this.state;
const isInvalid =
passwordOne !== passwordTwo ||
passwordOne === '' ||
username === '' ||
email === '';
return (
<form onSubmit={this.onSubmit}>
<input
value={username}
onChange={event => this.setState(updateByPropertyName('username', event.target.value))}
type="text"
placeholder="Full Name"
/>
<input
value={email}
onChange={event => this.setState(updateByPropertyName('email', event.target.value))}
type="text"
placeholder="Email Address"
/>
<input
value={passwordOne}
onChange={event => this.setState(updateByPropertyName('passwordOne', event.target.value))}
type="password"
placeholder="Password"
/>
<input
value={passwordTwo}
onChange={event => this.setState(updateByPropertyName('passwordTwo', event.target.value))}
type="password"
placeholder="Confirm Password"
/>
<button disabled={isInvalid} type="submit">
Sign Up
</button>
{ error && <p>{error.message}</p> }
</form>
);
}
}
const SignUpLink = () =>
<p>
Don't have an account?
{' '}
<Link to={routes.SIGN_UP}>Sign Up</Link>
</p>
const mapDispatchToProps = dispatch => bindActionCreators({ signup }, dispatch)
export default connect(null, mapDispatchToProps)(withRouter(SignUpPage));
export {
SignUpForm,
SignUpLink,
};
Its not a prop,
you've imported it as a function,
you can directly use it as function like this
import {signup} from './../../actions/signup';
.....
auth.doCreateUserWithEmailAndPassword(email, passwordOne)
.then(authUser => {
const userid = authUser.user.uid;
const user = { email, userid };
signup(user);
})
.catch(error => {
this.setState(updateByPropertyName('error', error));
});