Upload and send data with axios in reactjs - reactjs

How can i get data from input with type file and send it with axios in reactjs?
I found something about formData but i didn't find anything about get data from input and send it with axios.
thanks.

Lets assume that you have all the input data along with the file in your state like
constructor(props) {
super(props);
this.state = {
file : someName.txt, // file input
stateName : 'MP' // Text Input
date : 07/08/2018 // Date input
}
}
Now, in you handelSubmit method construct a JSON Object
handelSubmit = () => {
const { file, stateName, date } = this.state;
let data = [];
data['file'] = file;
data['stateName'] = stateName;
data['date'] = date;
// a function which makes a axios call to API.
uploadFile(data, (response) => {
// your code after API response
});
}
Here is a function to make a API call by axios
uploadFile(data, callback) {
const url = ''; // url to make a request
const request = axios.post(url, data);
request.then((response) => callback(response));
request.catch((err) => callback(err.response));
}
UPDATED :
Text On Change method to set state
handelOnChange = (event) => {
const target = event.target;
const value = target.value;
const name = target.name;
this.setState({
[name]: value
});
}
Method on upload of file to set into state
handelOnUploadFile = (event) => {
this.setState({
file : event.target.files
})
}
Here is a JSX code.
render() {
return(
<div>
<input type="file" onChange={this.handelOnUploadFile} /> {/* input tag which to upload file */}
<input type="text" name="stateName" onChange={this.handelOnChange} /> {/* text input tag */}
<button type="submit" onClick={this.handelSubmit}> UPLOAD </button>
</div>
)
}
Hope it helps you.

Related

input type file doesn't act independently for different children in a map in react js

I am iterating a list of file inputs in react through a map function
render() {
const {
form,
loading,
filledIndex,
} = this.state;
return (
map function
{form.a &&
form.a.map((prop, index) => (
upload component
<label
htmlFor="proofDocUrl"
className="save-form-btn-personal"
>
Upload
</label>
<input
type="file"
accept=".png,.jpg"
id="proofDocUrl"
name="proofDocUrl"
onChange={(e) => this.handleChangeUpload(e, index)}
onClick={(event) => {
event.target.value = null;
}}
/>
handleChangeUpload function. send file to server and fetch url
handleChangeUpload = async (e, index) => {
if (e.target.files) {
let formData = new FormData();
formData.append("b", e.target.files[0]);
this.setState({ loading: true });
const form = { ...this.state.form };
let documentUpload =
await A_service.A_api(formData);
if (documentUpload) {
if (documentUpload.data && documentUpload.data.data) {
documentUpload = documentUpload.data.data;
}
form.x[index][e.target.name] =
documentUpload.parameter;
}
this.setState({ loading: false, form });
}
};
states that are used. form state has x component which is iterated through a map.
proofDocUrl stores image url
form: {
x: [],
},
formLocal: {
proofDocUrl: "",
},
when a new object is added to form.x state and when it creates a new child in render the file input acts as if its the same for all childeren.
when a image is uploaded in one file component in one child, same will be uploaded for all childern.
How to make each file component in each child acts independently.

react cannot use fileinput twice without refreshing the page

I'm using html fileinput to upload a file with reactjs, but once I uploaded a file, I cannot call the function to upload another file, unless I refresh the page of course.
A simplified version of my code would be:
class Matrice extends React.Component {
constructor(props) {
super(props);
this.fileInput = null;
}
uploadQuestion = async e => {
console.log("uploading question");
if (e.target.files[0]) {
const form = new FormData();
let type;
if (e.target.files[0].type == "image/jpeg") type = ".jpg";
if (e.target.files[0].type == "image/png") type = ".png";
if (e.target.files[0].type == "image/gif") type = ".gif";
// const fileName = this.props.current + type;
form.append("files", e.target.files[0]); //filename
form.append("ref", "exam"); // model
form.append("refId", this.props.match.params.id); // id
form.append("field", "media"); // name of field (image field)
this.setState({ questionUploadLoading: true });
const files = await strapi.upload(form);
this.saveMontage(files, undefined, "question");
}
};
render() {
return (
<>
<input
style={{ display: "none" }}
ref={fileInput => (this.fileInput = fileInput)}
onChange={this.uploadQuestion}
className="file"
type="file"
id="imgAdd"
/>
<button
onClick={() => this.fileInput.click()}
type="button"
className="btn btn-secondary"
>
<i className="fas fa-image" />
</button>
</>
);
}
}
But my function uploadQuestion cannot be called again once I finished uploading a file. Namely, the console.log('uploading question') doesn't show up (the second time).
I don't know what could be the reason, but I guess that something is preventing the onChange handler as if, uploading a file the second time doesn't "changes" the trigger.
Does anybody have an idea what could cause this?
Thanks
You can reset the file input by setting its value to the empty string, and you will be able to use it again.
uploadQuestion = async (e) => {
console.log('uploading question')
if (e.target.files[0]) {
// ...
this.fileInput.value = "";
}
}
You need to set the state for image that to be upload there is flow the step
Set a state for upload file in your Constructor (uploadFile:null)
Add a function for handle file Change
Use state upload(uploadFile) into uploadQuestion() instead of e.target.value[0]
After Upload setState back to uploadFile:null
set the file input onChange={this.fileHandle}
class Matrice extends React.Component {
constructor(props) {
super(props);
this.state:{
uploadFile:null
}
this.fileInput = null;
this.fileHandle = this.fileHandle.bind(this)
}
fileHandle (e, a) {
e.preventDefault()
this.setState({ upload: e.target.files[0] })
};
uploadQuestion = async (e) => {
console.log('uploading question')
if (e.target.files[0]) {
const form = new FormData();
let type;
if (e.target.files[0].type == 'image/jpeg') type = '.jpg'
if (e.target.files[0].type == 'image/png') type = '.png';
if (e.target.files[0].type == 'image/gif') type = '.gif';
// const fileName = this.props.current + type;
//Use state upload(uploadFile) into uploadQuestion() instead of e.target.value[0]
file.append('images', this.state.uploadFile, this.state.uploadFile.name) //filename
form.append('ref', 'exam'); // model
form.append('refId', this.props.match.params.id) // id
form.append('field', 'media') // name of field (image field)
this.setState({questionUploadLoading: true})
const files = await strapi.upload(form);
this.saveMontage(files, undefined, 'question')
//After Upload setState back to uploadFile:null
this.setState({uploadFile:null})
}
}
if you like to valid in onChange you can modify function as Below
fileHandle (e) {
e.preventDefault()
if (!e.target.files[0].name.match(/.(jpg|jpeg|png|gif)$/i)) {
this.setState({ errorMsg: 'Please upload valid file. Allowed format jpg, jpeg, png, gif' })
return false
} else {
this.setState({ upload: e.target.files[0], errorMsg: '' })
}
};
I had a heck of a time with this and no matter what I did from above nothing worked. Now, I've simply hardcoded the value to an empty string and I can upload over and over. I'm not even sure why this works, but I don't ever need the text value. The server cares about that. Here's a styled button using Material-UI where you never see the input, but you can upload over and over (in my case the server sends back some error and please fix your xlsx file message and I needed the user to be able to fix and try again):
import React from 'react';
import { Button } from '#material-ui/core';
import BackupIcon from '#material-ui/icons/Backup';
const UploadButton = ({ onChange, name, label, disabled }) => {
return (
<div className={'MuiFormControl-root MuiTextField-root'}>
<input
name={name}
id='contained-button-file'
type='file'
accept='.csv, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
style={{ display: 'none' }}
onChange={onChange}
disabled={disabled}
value=''
/>
<label htmlFor='contained-button-file'>
<Button
color='primary'
aria-label='Upload scan file.'
variant='contained'
component='span'
startIcon={<BackupIcon />}
disabled={disabled}
>
{label}
</Button>
</label>
</div>
);
};
export default UploadButton;
Just handle it using click event
const handleClick = event => {
const { target = {} } = event || {};
target.value = "";
};
<input type="file" onChange={handleChange} onClick={handleClick} />

how to delete a single item using axios in react

I have looked into many articles and posts like this but it does not work in my case.I simply need to delete an item from my post list in my application using axios. In the axios docs it says you need to pass in the params to the delete method. Also I have sen in most apps they use ids without having ids in their state. But i cannot get it working. Please see my entire code. I know that my delete method is wrong please help me fix it:
// The individual post component
const Post = props => (
<article className="post">
<h2 className="post-title">{props.title}</h2>
<hr />
<p className="post-content">{props.content}</p>
<button onClick={props.delete}>Delete this post</button>
</article>
);
// The seperate form component to be written later
class Form extends React.Component {}
// The posts loop component
class Posts extends React.Component {
state = {
posts: [],
post: {
title: "",
content: ""
}
// error:false
};
componentDidMount() {
const { posts } = this.state;
axios
.get("url")
.then(response => {
const data = Object.values(response.data);
this.setState({ posts : data });
});
}
handleChange = event => {
const [name , value] = [event.target.name, event.target.value];
// const value = event.target.value;
const { post } = this.state;
const newPost = {
...post,
[name]: value
};
this.setState({ post: newPost });
};
handleSubmit = event => {
event.preventDefault();
const {post} = this.state;
const {posts} = this.state;
axios
.post("url", post)
.then(response => {
// console.log(response);
const newPost = Object.values(response.data);
this.setState({ post: newPost });
const updatedPosts = [...posts, {title:post.title,content:post.content}];
this.setState({ posts: updatedPosts});
// console.log(post);
console.log(updatedPosts);
console.log(this.state.posts);
});
};
handleDelete = () => {
const { post } = this.state;
axios.delete("url",{params: {id: post.id}})
.then(response => {
console.log(response);
});
};
render() {
let posts = <p>No posts yet</p>;
if (this.state.posts !== null) {
posts = this.state.posts.map(post => {
return <Post
key={post.id}
{...post}
delete={this.handleDelete}/>;
});
}
return (
<React.Fragment>
{posts}
<form className="new-post-form" onSubmit={this.handleSubmit}>
<label>
Post title
<input
className="title-input"
type="text"
name="title"
onChange={this.handleChange}
/>
</label>
<label>
Post content
<input
className="content-input"
type="text"
name="content"
onChange={this.handleChange}
/>
</label>
<input className="submit-button" type="submit" value="submit" />
</form>
</React.Fragment>
);
}
}
I also see this Error in console:
Uncaught (in promise) TypeError: Cannot convert undefined or null to object
at Function.values which is in get method.
Thanks again.
You are not specifying what your Post component should delete. In other words, the props.delete is not receiving an id to pass up to your parent component. In order to do that, you can change that to () => props.delete(props.id) and then in your parent component you need to have the handleDelete method receive the id of the item you want to target which is the id we passed up earlier from Post.
I don't know how your server is set up but using the axios request you originally have in your question your code would look like this:
handleDelete = (itemId) => {
// Whatever you want to do with that item
axios.delete("url", { params: { id: itemId } }).then(response => {
console.log(response);
});
Here's a CodeSandbox (using some dummy data in the constructor) displaying the item being passed in a console.log() (axios statement is commented out).
EDIT: How to make axios delete requests using Firebase REST API
Oh sorry, I did not see that you were using Firebase. Direct REST requests are a bit different with Firebase. In your configuration the requests should look like this:
axios.delete(`${url}/${firebasePostId}.json`).then(response => {
console.log(response)
})
This is assuming your Firebase rules allow unauthorized requests (which I strongly advise against, seeing as anyone could send this request).
Please note that firebasePostId is the push key provided by Firebase when you send POST requests to them, and are in fact a great choice of id for your posts. An example of one is -LOLok8zH3B8RonrWdZs which you mentioned in the comments.
For more information on Firebase REST API syntax, check out their documentation.
Thanks #FranklinFarahani. I had to write an answer as it is too long. I have changed my get and post method and managed to fix the delete method. I use the unique key that firebase is creating per post to delete each item. I get that inget method. This is the entire code.
// The individual post component
const Post = props => (
// use the key as an id here
<article id={props.id} className="post">
<h2 className="post-title">{props.title}</h2>
<hr />
<p className="post-content">{props.content}</p>
<button onClick={props.delete}>Delete this post</button>
</article>
);
// The Post lists component
class Posts extends React.Component {
state = {
posts: [],
post: {
id: "",
title: "",
content: ""
},
indexes: []
};
componentDidMount() {
const { posts } = this.state;
axios
.get("firebaseURL/posts.json")
.then(response => {
// create an array to hold th unique id as key and post as value using Object.entries
const retrievedPosts = [];
for (const [key, value] of Object.entries(response.data)) {
const post = {
id: key,
title: value.title,
content: value.content
};
// add allposts to the array here
retrievedPosts.push(post);
}
// update state
this.setState({ posts: retrievedPosts });
console.log(retrievedPosts);
});
}
handleChange = event => {
const [name, value] = [event.target.name, event.target.value];
// const value = event.target.value;
const { post } = this.state;
const newPost = {
...post,
[name]: value
};
this.setState({ post: newPost });
};
handleSubmit = event => {
event.preventDefault();
const { posts } = this.state;
// use this as a temporary id for post method
const postIndex = posts.length + 1;
const post = {
id: postIndex,
title: this.state.post.title,
content: this.state.post.content
};
axios
.post("firebaseURL/posts.json", post)
.then(response => {
const updatedPosts = [
...posts,
{ id: post.id, title: post.title, content: post.content }
];
// update state
this.setState({ posts: updatedPosts });
console.log(posts);
});
};
handleDelete = postId => {
event.preventDefault();
// get a copy of the posts
const posts = [...this.state.posts];
// in delete method use postId to create a unique url for the post to be deleted
axios
.delete(
"firebaseURL/posts/" + postId + ".json"
)
.then(response => {
//update state
this.setState({ posts: posts });
});
};
render() {
let posts = <p>No posts yet</p>;
if (this.state.posts !== null) {
posts = this.state.posts.map(post => {
return (
<Post
id={post.id}
key={post.id}
{...post}
delete={() => this.handleDelete(post.id)}
/>
);
});
}
return (
<React.Fragment>
{posts}
<form className="new-post-form" onSubmit={this.handleSubmit}>
<label>
Post title
<input
className="title-input"
type="text"
name="title"
onChange={this.handleChange}
/>
</label>
<label>
Post content
<textarea
className="content-input"
rows="7"
type="text"
name="content"
onChange={this.handleChange}
/>
</label>
<input className="submit-button" type="submit" value="submit" />
</form>
</React.Fragment>
);
}
}
The problem is my state is not updated after delete so although the post has been deleted from my database it is still in the DOM.
And more importantly if submit a new post cannot delete it after a get request or refresh being done. The reason is in post request the key will be created after the request is done and therefor I will not have the key to update state and DOM until after the next get request or refresh. And the id will be the temporary one which I assign during post method which cannot be used to delete a post.

ReactJS file upload

I am trying to upload a file using reactjs. I am not getting the right log. Before uploading, I wanted to see the output. But not getting the result.
Here what I have tried
state = {
selectedFile: null
}
fileChangedHandler = event => {
this.setState({
selectedFile: event.target.files[0]
})
console.log(this.state.selectedFile)
}
uploadHandler = () => {
const formData = new FormData()
var fd = formData.append("data", this.state.selectedFile, this.state.selectedFile.name)
console.log(fd)
}
render() {
return (
<div>
<input type="file" onChange={this.fileChangedHandler} />
<button onClick={this.uploadHandler}>Upload!</button>
</div>
);
}
Try this
// Create your FormData object
var formData = new FormData();
formData.append('key1', 'value1'); // Test data
formData.append('key2', 'value2'); // Test data
// Display the key/value pairs array
for (var pair of formData.entries()) {
console.log(pair);
}

How to open a page in new tab on click of a button in react? I want to send some data to that page also

I'm working on a raise invoice page, in which user can raise a invoice on clicking of a button, I would call a api call and after getting the response I want to send some data to a page(RaisedInvoice.jsx) which should open in a new tab, how can i do it. The thing which I am not getting is how to open a page in new tab on click of a button in ReactJs.
RaiseInvoice.jsx:
import React from 'react';
import Links from './Links.jsx';
import history from './history.jsx';
import axios from 'axios';
class RaiseInvoice extends React.Component {
constructor(props) {
super(props);
// This binding is necessary to make `this` work in the callback
this.state = {projects: [], searchParam : ''};
this.raiseInvoiceClicked = this.raiseInvoiceClicked.bind(this);
}
raiseInvoiceClicked(){
// here i wish to write the code for opening the page in new tab.
}
render() {
return (
<div>
<Links activeTabName="tab2"></Links>
<div className="container">
<div className = "row col-md-4">
<h1>Raise Invoice...</h1>
</div>
<div className = "row col-md-4"></div>
<div className = "row col-md-4" style ={{"marginTop":"24px"}}>
<button type="button" className="btn btn-default pull-right" onClick={this.raiseInvoiceClicked}>Raise Invoice</button>
</div>
</div>
</div>
)
}
}
export default RaiseInvoice;
Since you were going to send big data, appending them to your target URL looks shabby. I would suggest you use 'LocalStorage' for this purpose. So your code looks like this,
raiseInvoiceClicked(){
// your axios call here
localStorage.setItem("pageData", "Data Retrieved from axios request")
// route to new page by changing window.location
window.open(newPageUrl, "_blank") //to open new page
}
In your RaisedInvoice.jsx, retrieve the data from Local Storage like this,
componentWillMount() {
localStorage.pagedata= "your Data";
// set the data in state and use it through the component
localStorage.removeItem("pagedata");
// removing the data from localStorage. Since if user clicks for another invoice it overrides this data
}
You can just use plain JS to do it and append some query perimeters with it
raiseInvoiceClicked(){
const url = 'somesite.com?data=yourDataToSend';
window.open(url, '_blank');
}
Instead of calling raiseInvoiceClicked() function inside onclick method, you can try
onClick="window.open('your_url')"
in your code.
Simply do this!
const openLinkInNewTab = ( url ) => {
const newTab = window.open(url, '_blank', 'noopener,noreferrer');
if ( newTab ) newTab.opener = null;
}
//...
return (
//...
<button onClick={ () => openLinkInNewTab('your.url')}> Click Here </button>
//...
)
You can open it in a new window using the following code.
Please note that for props you can pass any child components that should be rendered inside new window.
const RenderInWindow = (props) => {
const [container, setContainer] = useState(null);
const newWindow = useRef(null);
useEffect(() => {
// Create container element on client-side
setContainer(document.createElement("div"));
}, []);
useEffect(() => {
// When container is ready
if (container) {
// Create window
newWindow.current = window.open(
"",
"",
"width=600,height=400,left=200,top=200"
);
// Append container
newWindow.current.document.body.appendChild(container);
// Save reference to window for cleanup
const curWindow = newWindow.current;
// Return cleanup function
return () => curWindow.close();
}
}, [container]);
return container && createPortal(props.children, container);
};
Pass this data with props:
let href = '...url_to_redirect...'; let data_to_send = [...];
let api_href = '...url_api_where_sent_data.../?data_to_send';
export const DictDefaultOptions = (url=(api_href), method='GET') => {
let defaultOptions = {
url : url,
method : method,
mode : 'cors',
headers : {'Access-Control-Allow-Origin':'*'}
};
return defaultOptions };
let sentData = {
method: defaultOptions.method,
mode: defaultOptions.mode
};
send_data_to_api = () => {
let api_return = await fetch(api_href, sentData)
.then(response => response.json())
.then(responseText => {
data = (JSON.parse(JSON.stringify(responseText)))
})
.catch(error => {
console.log(`${requestURL} error: `, error)
});
do { await this.wait(100) } while(Object.keys(api_return).length <= 0);
if (Object.keys(api_return).length > 0) {
return window.open(href, "_blank")
}
};

Resources