undefined error when trying to set state - reactjs

I'm trying to upload images and then set the state for each image uploaded.
However, I keep on getting this error when hitting the 'Upload' button:
catch: TypeError: Cannot read property 'setState' of undefined
The error is occurring in this block of code:
.then(function (response) {
//handle success
console.log('then: ', response);
this.setState({
file: e.target.files[0]
});
})
Any help would be appreciated!
Thanks!
Here is the entire component:
import React, { Component } from 'react';
import axios from 'axios';
class ImageUpload extends Component {
constructor(props) {
super(props);
this.state = { file: '', imagePreviewUrl: '' };
}
_handleSubmit(e) {
e.preventDefault();
let fd = new FormData();
fd.append('image', this.state.file);
const config = {
headers: { 'content-type': 'multipart/form-data' }
}
const postData = {
method: 'POST',
credentials: 'include',
body: fd
}
axios({
method: 'post',
url: `/api/gaming/profiles/${this.props.profileId}/files/upload`,
data: postData
})
.then(function (response) {
//handle success
console.log('then: ', response);
this.setState({
file: event.target.files[0]
});
})
.catch(function (response) {
//handle error
console.log('catch: ', response);
});
}
_handleImageChange(e) {
e.preventDefault();
let reader = new FileReader();
let file = e.target.files[0];
reader.onloadend = () => {
this.setState({
file: file,
imagePreviewUrl: reader.result
});
}
reader.readAsDataURL(file)
}
render() {
const { questionId } = this.props;
let { imagePreviewUrl } = this.state;
let $imagePreview = null;
if (imagePreviewUrl) {
$imagePreview = (<img src={imagePreviewUrl} />);
} else {
$imagePreview = (<div className="previewText">Please select an Image for Preview</div>);
}
console.log('ImageUpload Render()');
return (
<div className="previewComponent">
<input className="fileInput"
type="file"
onChange={(e) => this._handleImageChange(e)} />
<button className="submitButton"
type="submit"
onClick={(e) => this._handleSubmit(e)}>Upload Image</button>
<div className="imgPreview">
{$imagePreview}
</div>
</div>
);
}
}
export default ImageUpload;

Change your Promise resolver to an arrow function, it'll lexically scope this for you.
Change it to:
.then((response) => {
//handle success
console.log('then: ', response);
this.setState({
file: e.target.files[0]
});
})

In your _handleSubmit(e) define var self = this
then use self.setState instead of this.setState
_handleSubmit(e) {
e.preventDefault();
let fd = new FormData();
fd.append('image', this.state.file);
const config = {
headers: { 'content-type': 'multipart/form-data' }
}
const postData = {
method: 'POST',
credentials: 'include',
body: fd
}
var self = this
axios({
method: 'post',
url: `/api/gaming/profiles/${this.props.profileId}/files/upload`,
data: postData
})
.then(function (response) {
//handle success
console.log('then: ', response);
self.setState({
file: event.target.files[0]
});
})
.catch(function (response) {
//handle error
console.log('catch: ', response);
});
}

Related

How to take value from onChange to Upload Image in React

Hello guys I got the following 2 functions:
changeProfileImage=(event)=>{
this.setState({file:event.target.files[0]});
}
and the upload one:
upload(file) {
let formData = new FormData();
const username1 = localStorage.getItem('username')
formData.append("file", file);
return axios({
method: 'post',
url: 'http://localhost:3000/users/addUserImage/'+username1,
data: formData,
headers: {
'Content-Type': `multipart/form-data; boundary=${formData._boundary}`,
},
});
}
Main scope is to update the username1 image with the given one from changeProfileImage function. How can I make my Upload function taking the setState file ?
You can pass the file value as an argument to the upload function when you call it.
code pen: https://codepen.io/akartit/pen/PoaXgQO
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
file: null
};
this.changeProfileImage = this.changeProfileImage.bind(this);
this.upload = this.upload.bind(this);
}
changeProfileImage = (event) => {
// Set the file value in the state
this.setState({ file: event.target.files[0] });
};
upload = (file) => {
let formData = new FormData();
const username1 = localStorage.getItem("username");
formData.append("file", file);
return axios({
method: "post",
url: "https://webhook.site/c62f75b5-32be-4f3f-a6cd-e84f515706f4",
data: formData,
headers: {
"Content-Type": `multipart/form-data; boundary=${formData._boundary}`
}
});
};
render() {
return (
<div>
<input type="file" onChange={this.changeProfileImage} />
<button onClick={() => this.upload(this.state.file)}>Upload</button>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));

How to make file upload in React

i have api like :
static async addTenderAttachment(documentId: number, file: File) {
const request = baseApi.getClient();
const formData = new FormData();
formData.append('attachments', file, file.name);
const options: AxiosRequestConfig = {
url: `/clients/Documents/Requests/${documentId}/Attachments`,
method: 'POST',
headers: {
'Content-Type': 'multipart/form-data',
},
data: formData,
};
try {
const response: any = await request(options);
console.log(response);
return { response };
} catch (error) {
return { error };
}
}
also have upload component :
<div className={styles.col}>
<FileInput
label='DocumentFile'
placeholder={
attachments[0]
? attachments[0].name
: ' No file'
}
onChange={onAttachFile}
/>
and have onChange for that component :
const onAttachFile = useCallback((file: File) => {
if (file !== null) {
AttachmentApi.addTenderAttachment(
formValues.id,
file,
).then((resp) => {
console.log('successfully added file ', resp);
getAttachments();
});
}
}, []);
So as you see, i can add file only to existing tender, because only existing ones have documentId,
on createTender window there is no id, only after creating it appears.
Question is, how to make file upload work without id number..?

Pick images in react native and upload it

I want to upload an array of images using react-native-image-crop-picker but I can't fix it.
What I've tried:
Fetch (javascript) and also RN-fetch-blob but no luck
At first the catch error was network error
and now the problem is that I'm sending an empty array to the server
here is my code:
export default class Upload extends Component {
constructor() {
super();
this.state = {
token: '',
photos: [],
};
}
_TakePhoto() {
ImagePicker.openPicker({
multiple: true,
}).then((images) => {
images.map((item, index) => {
ImageResizer.createResizedImage(item.path, 1200, 1200, 'JPEG', 100)
.then((response) => {
// console.warn('Resized img is: ', response);
this.state.photos.push(response);
console.warn('Resized img state is: ', this.state.photos);
this._submit_pictures();
})
.catch((err) => {});
});
});
}
_submit_pictures() {
let formData = new FormData();
for (let i = 0; i < this.state.photos.length; i++) {
let file = {
uri: this.state.photos[0].path,
// uri: this.state.photos[0].path.replace('file:///', ''),
// uri: this.state.photos[0].uri,
type: this.state.photos[0].mime,
name: this.state.photos[0].name,
};
formData.append('pics[]', file);
}
// uri: this.state.photos[0].uri.replace("file:///", "file://"),
formData.append('postId', postId);
formData.append('token', token);
console.log('formData value: ', formData);
axios({
url: 'https://rahnama.com/webservice/submitPictures',
method: 'POST',
headers: {
// "Accept": "application/json",
'Content-Type': 'multipart/form-data',
},
// formData
body: formData,
})
.then((response) => {
console.warn('upload res: ', response);
})
.catch((error) => console.warn('upload err: ', error.response.request._response));
}
render() {
return <Text onPress={() => this._TakePhoto()}> Pick </Text>;
}
}
Solved it by sending the image data in base64.
1- Pick the image
2- convert it to base64
3- pass the base64 string as the payload

React - TypeError: Cannot read property 'includes' of undefined

I'm new to React, sorry if this is too basic.
I have an input form and I'm trying to handle submits and changes to it, like so:
import { editMenuFormRules } from './forms/form-rules.js';
class Seeds extends Component{
constructor (props) {
super(props);
this.state = {
formData: {
coffee:''
},
menu:[],
editMenuFormRules:editMenuFormRules,
};
this.handleSubmitCoffees = this.handleSubmitCoffees.bind(this);
this.handleBeanFormChange = this.handleBeanFormChange.bind(this);
};
componentDidMount() {
if (this.props.isAuthenticated) {
this.getSeeds();
}
};
getSeeds(event) {
const {userId} = this.props
const options = {
url: `${process.env.REACT_APP_WEB_SERVICE_URL}/seeds/${userId}`,
method: 'get',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${window.localStorage.authToken}`
}
};
return axios(options)
.then((res) => {
console.log(res.data.data)
this.setState({
menu: res.data.data[0].menu
})
})
.catch((error) => { console.log(error); });
};
for handling submit and form change, I have:
handleSubmitCoffees(event) {
event.preventDefault();
const formType = this.props.formType
const {userId} = this.props
var headers = {
'Content-Type': 'application/json',
Authorization: `Bearer ${window.localStorage.authToken}`
}
const data = {
coffee: this.state.formData.coffee
};
if (formType === 'EditMenu' && this.state.menu.includes(this.state.formData.coffee)) {
alert('This coffee already exists. Please add a new one.');
return (<Redirect to='/seeds' />);
};
const url = `${process.env.REACT_APP_WEB_SERVICE_URL}/edit_menu/${userId}`;
axios.post(url, data, headers)
.then((res) => {
this.clearForm()
console.log(data);
})
.catch((err) => {
if (formType === 'EditCoffee') {
this.props.createMessage('Coffee edit failed.', 'Please review your entry');
};
});
};
and:
handleBeanFormChange(event) {
console.log(event)
const obj = this.state.formData;
obj[event.target.name] = event.target.value;
this.setState(obj);
this.validateForm();;
};
finally, my form:
<form onSubmit={ (event) => this.handleSubmitCoffees(event) }>
<div className="field">
<input
name="coffee"
className="input is-large"
type="text"
placeholder="Enter Coffee Name"
value={this.state.formData.coffee}
onChange={this.handleBeanFormChange}
/>
</div>
</form>
when I input my first item at form, however, I'm getting the following error:
TypeError: Cannot read property 'includes' of undefined
which points to this line:
> 150 | if (formType === 'EditMenu' && this.state.menu.includes(this.state.formData.coffee)) {
am I not defining this.state.formData.coffee when I press enter at form?
What am I missing?
const obj = this.state.formData;
obj[event.target.name] = event.target.value;
this.setState(obj); // <-- this is setting 'target.name: value'
This is effectively overwriting formData. I think what you are meaning to do is:
const obj = Object.assign({}, this.state.formData);
obj[event.target.name] = event.target.value;
this.setState({ formData: obj });
Do note that it's important to clone the formData object, as what you are doing is mutating the state, which is not desired.
The problem is here, where there is a GET request for 'menu' value:
componentDidMount() {
if (this.props.isAuthenticated) {
this.getSeeds(); ///// <------------
}
};
There was a malformed json response object at backend:
response_object = {
'status': 'success',
'message': 'User does not have a menu yet',
'data': [{"id": user.id,
"seeds": user.seeds,
"content": template}]
}
//"menu": [] //----> key, value was missing
Therefore, there was no 'menu' value being fetched, and that's what 'undefined' refers to:
//GET request at `getSeeds()`
this.setState({
menu: res.data.data[0].menu // <----- undefined
})
no this.state.menu could be set at getSeeds(), at all.
Adding "menu": [] to response_object fixed it.

React.js, how to send a multipart/form-data to server

We want to send an image file as multipart/form to the backend, we try to use html form to get file and send the file as formData, here are the codes
export default class Task extends React.Component {
uploadAction() {
var data = new FormData();
var imagedata = document.querySelector('input[type="file"]').files[0];
data.append("data", imagedata);
fetch("http://localhost:8910/taskCreationController/createStoryTask", {
mode: 'no-cors',
method: "POST",
headers: {
"Content-Type": "multipart/form-data"
"Accept": "application/json",
"type": "formData"
},
body: data
}).then(function (res) {
if (res.ok) {
alert("Perfect! ");
} else if (res.status == 401) {
alert("Oops! ");
}
}, function (e) {
alert("Error submitting form!");
});
}
render() {
return (
<form encType="multipart/form-data" action="">
<input type="file" name="fileName" defaultValue="fileName"></input>
<input type="button" value="upload" onClick={this.uploadAction.bind(this)}></input>
</form>
)
}
}
The error in backend is "nested exception is org.springframework.web.multipart.MultipartException: Could not parse multipart servlet request; nested exception is java.io.IOException: org.apache.tomcat.util.http.fileupload.FileUploadException: the request was rejected because no multipart boundary was found".
After reading this, we tried to set boundary to headers in fetch:
fetch("http://localhost:8910/taskCreationController/createStoryTask", {
mode: 'no-cors',
method: "POST",
headers: {
"Content-Type": "multipart/form-data; boundary=AaB03x" +
"--AaB03x" +
"Content-Disposition: file" +
"Content-Type: png" +
"Content-Transfer-Encoding: binary" +
"...data... " +
"--AaB03x--",
"Accept": "application/json",
"type": "formData"
},
body: data
}).then(function (res) {
if (res.ok) {
alert("Perfect! ");
} else if (res.status == 401) {
alert("Oops! ");
}
}, function (e) {
alert("Error submitting form!");
});
}
This time, the error in backend is: Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause
Do we add the multipart boundary right? Where should it be?
Maybe we are wrong at first because we don't get the multipart/form-data. How can we get it correctly?
We just try to remove our headers and it works!
fetch("http://localhost:8910/taskCreationController/createStoryTask", {
mode: 'no-cors',
method: "POST",
body: data
}).then(function (res) {
if (res.ok) {
alert("Perfect! ");
} else if (res.status == 401) {
alert("Oops! ");
}
}, function (e) {
alert("Error submitting form!");
});
Here is my solution for image upload with preview through axios.
import React, { Component } from 'react';
import axios from "axios";
React Component Class:
class FileUpload extends Component {
// API Endpoints
custom_file_upload_url = `YOUR_API_ENDPOINT_SHOULD_GOES_HERE`;
constructor(props) {
super(props);
this.state = {
image_file: null,
image_preview: '',
}
}
// Image Preview Handler
handleImagePreview = (e) => {
let image_as_base64 = URL.createObjectURL(e.target.files[0])
let image_as_files = e.target.files[0];
this.setState({
image_preview: image_as_base64,
image_file: image_as_files,
})
}
// Image/File Submit Handler
handleSubmitFile = () => {
if (this.state.image_file !== null){
let formData = new FormData();
formData.append('customFile', this.state.image_file);
// the image field name should be similar to your api endpoint field name
// in my case here the field name is customFile
axios.post(
this.custom_file_upload_url,
formData,
{
headers: {
"Authorization": "YOUR_API_AUTHORIZATION_KEY_SHOULD_GOES_HERE_IF_HAVE",
"Content-type": "multipart/form-data",
},
}
)
.then(res => {
console.log(`Success` + res.data);
})
.catch(err => {
console.log(err);
})
}
}
// render from here
render() {
return (
<div>
{/* image preview */}
<img src={this.state.image_preview} alt="image preview"/>
{/* image input field */}
<input
type="file"
onChange={this.handleImagePreview}
/>
<label>Upload file</label>
<input type="submit" onClick={this.handleSubmitFile} value="Submit"/>
</div>
);
}
}
export default FileUpload;
The file is also available in the event:
e.target.files[0]
(eliminates the need for document.querySelector('input[type="file"]').files[0];)
uploadAction(e) {
const data = new FormData();
const imagedata = e.target.files[0];
data.append('inputname', imagedata);
...
Note:
Use console.log(data.get('inputname')) for debugging, console.log(data) will not display the appended data.
https://muffinman.io/uploading-files-using-fetch-multipart-form-data/ worked best for me. Its using formData.
import React from "react";
import logo from "./logo.svg";
import "./App.css";
import "bootstrap/dist/css/bootstrap.min.css";
import Button from "react-bootstrap/Button";
const ReactDOM = require("react-dom");
export default class App extends React.Component {
constructor(props) {
super(props);
this.test = this.test.bind(this);
this.state = {
fileUploadOngoing: false
};
}
test() {
console.log(
"Test this.state.fileUploadOngoing=" + this.state.fileUploadOngoing
);
this.setState({
fileUploadOngoing: true
});
const fileInput = document.querySelector("#fileInput");
const formData = new FormData();
formData.append("file", fileInput.files[0]);
formData.append("test", "StringValueTest");
const options = {
method: "POST",
body: formData
// If you add this, upload won't work
// headers: {
// 'Content-Type': 'multipart/form-data',
// }
};
fetch("http://localhost:5000/ui/upload/file", options);
}
render() {
console.log("this.state.fileUploadOngoing=" + this.state.fileUploadOngoing);
return (
<div>
<input id="fileInput" type="file" name="file" />
<Button onClick={this.test} variant="primary">
Primary
</Button>
{this.state.fileUploadOngoing && (
<div>
<h1> File upload ongoing abc 123</h1>
{console.log(
"Why is it printing this.state.fileUploadOngoing=" +
this.state.fileUploadOngoing
)}
</div>
)}
</div>
);
}
}
React File Upload Component
import { Component } from 'react';
class Upload extends Component {
constructor() {
super();
this.state = {
image: '',
}
}
handleFileChange = e => {
this.setState({
[e.target.name]: e.target.files[0],
})
}
handleSubmit = async e => {
e.preventDefault();
const formData = new FormData();
for (let name in this.state) {
formData.append(name, this.state[name]);
}
await fetch('/api/upload', {
method: 'POST',
body: formData,
});
alert('done');
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<input
name="image"
type="file"
onChange={this.handleFileChange}>
</input>
<input type="submit"></input>
</form>
)
}
}
export default Upload;
the request was rejected because no multipart boundary was found".
When you send multipart/form-data, the boundary is automatically added to a content-type of a request header. you have to tell the server when the parameter ends with the boundary rule. You had to set the Content-type like this
"Content-Type": `multipart/form-data: boundary=add-random-characters`
This article with guide you: https://roytuts.com/boundary-in-multipart-form-data/
The boundary is included to separate name/value pair in the
multipart/form-data. The boundary parameter acts like a marker for
each pair of name and value in the multipart/form-data. The boundary
parameter is automatically added to the Content-Type in the http
(Hyper Text Transfer Protocol) request header.
For sending multipart/formdata, you need to avoid contentType, since the browser automatically assigns the boundary and Content-Type.
In your case by using fetch, even if you avoid Content-Type it sets to default text/plain. So try with jQuery ajax. which removes the contentType if we set it to false.
This is the working code
var data = new FormData();
var imagedata = document.querySelector('input[type="file"]').files[0];
data.append("data", imagedata);
$.ajax({
method: "POST",
url: fullUrl,
data: data,
dataType: 'json',
cache: false,
processData: false,
contentType: false
}).done((data) => {
//resolve(data);
}).fail((err) => {
//console.log("errorrr for file upload", err);
//reject(err);
});

Resources