ReactJs - Axios : Uploading Image - reactjs

I hope you are well especially in the covid crisis.
im trying to upload an image using axios but it apears always to be null and i cant fix it.
i used encType="multipart/form-data" , and <meta name="csrf-token" content="{{ csrf_token() }}" and nothing works for me ; i think the problem is within the onChange{} despite its from the official ReactJs documentation.
here my component :
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import axios from 'axios';
export default class MedcineRegister extends Component
{
constructor(props)
{
super(props);
this.state = {
json:JSON.parse(props.data),//data received from a laravel controller used to implement the select option menu down below.
Specialite: '1',//initialization
image: '',
};
this.onChangeValue = this.onChangeValue.bind(this);
this.onSubmitButton = this.onSubmitButton.bind(this);
}
onChangeValue(e) {
this.setState({
[e.target.name]: e.target.value,//->this line is working only for Specialite
});
}
async onSubmitButton(e) {
e.preventDefault();
try {
const response = await axios.post('/medcine/registerR',{
Specialite: this.state.Specialite,
image: this.state.image,
});
console.log(response.data);//[{…}]0: {Specialite: "8" , image: null}
} catch (error) {
console.log("error in MedcineRegister.js");
}
}
componentDidMount () {
}
render()
{
return (
<div className="container">
<div className="card-body">
<form encType="multipart/form-data" onSubmit={this.onSubmitButton}>
<div className="col-md-6">
<select onChange={this.onChangeValue} name="Specialite" value={this.state.value} autoFocus>
{this.state.json.map(i => (
<option className="form-control" value={i.id}>{i.nom}</option>
))}
</select>
</div>
<div className="col-md-6">
<input id="file" type="file" name="file" onChange={this.onChangeValue} autoFocus/>
</div>
<div className="form-group row mb-0">
<button className="btn btn-primary">Submit</button>
</div>
</form>
</div>
</div>
);
}
}
if (document.getElementById('mr')) {
var data = document.getElementById(('mr')).getAttribute('data');
ReactDOM.render(<MedcineRegister data={data}/>, document.getElementById('mr'));
}
as you see guys the consol is alwas showing me "image" : null , any idea how to solve it please

The name attribute of your file input is "file".
So the this.setState in the onChangeValue is actually:
this.setState({
"file": e.target.value
});
Image is never being set.
And if it's a file that you want to post, there are a few changes to be made.
The setState in onChangeValue function should be:
this.setState({
image: e.target.files[0]
});
Data to be posted has to be sent as formData
const formData = new FormData();
formData.append("Specialite", this.state.Specialite);
formData.append("image", this.state.image);
const response = await axios.post("/medcine/registerR", formData, {
headers: {
"Content-Type": "multipart/form-data"
}
});

Related

I want to display the image sent by react axios

I want to send an image with react axios and return a character string, but I have a question here.
The backend is an API that puts an image and returns a character string.
I want to ask a question.
How can I display the image to be sent on the screen?
How can I display the string returned after sending the image?
import React from 'react'
import axios from 'axios';
class FileUpload extends React.Component{
constructor(){
super();
this.state = {
selectedFile:'',
}
this.handleInputChange = this.handleInputChange.bind(this);
}
handleInputChange(event) {
this.setState({
selectedFile: event.target.files[0],
})
}
submit(){
const data = new FormData()
data.append('file', this.state.selectedFile)
console.warn(this.state.selectedFile);
let url = "http://localhost:8000/upload.php";
axios.post(url, data, { // receive two parameter endpoint url ,form data
})
.then(res => { // then print response status
console.log(res.data)
})
}
render(){
return(
<div>
<div className="row">
<div className="col-md-6 offset-md-3">
<br /><br />
<h3 className="text-white">React File Upload Example - Tutsmake.com</h3>
<br />
<div className="form-row">
<div className="form-group col-md-6">
<label className="text-white">Select File :</label>
<input type="file" className="form-control" name="upload_file" onChange={this.handleInputChange} />
</div>
</div>
<div className="form-row">
<div className="col-md-6">
<button type="submit" className="btn btn-dark" onClick={()=>this.submit()}>名前確認</button>
</div>
</div>
</div>
</div>
</div>
)
}
}
export default FileUpload;
If I understood
this is how to get a preview of selected image
const [file] = e.target.files;
setImgPrev(URL.createObjectURL(file));
// imgPrev state will be the src of your preview image
and to get the string that returned after sending the html request you have to listen to the response that will come from axios
axios.post(url, data)
.then(res => setRes(res))
.catch(err => return err)
I've made image uploader and preview the uploaded image before sent to the BE with API. I also use normal html input with type file. May be my code can be used in yours.
const handleImageChange = (value) => {
let reader = new FileReader();
const image = value.target.files[0];
setSavedFile(image);
reader.onload = () => {
setMainImage({
img: reader.result,
});
};
reader.readAsDataURL(image);
}
...
...
<input
onChange={(e) => handleImageChange(e)
/>
the image that will rendered saved with setMainImage(), the image source is in reader.result. For the setSavedFile is used to save the file uploaded.
For the question number 2, you can normally save the response from the API to state and show the state in the html element

React passing data into Async Function from Input

So, I'm trying to pass data from an input element into an async function within my React App.js file. I'm having trouble understanding how to push the input value into the callAPI function.
At the moment I just have a dummy/placeholder ipaddress within the callAPI inorder to test the button is working and calling the function onClick. Here's my code..
import React from 'react';
import './App.css';
class App extends React.Component {
constructor(props) {
super(props);
this.state = { apiResponse: '' };
}
async callAPI() {
const ipaddress = '8.8.8.8';
const api_url = `http://localhost:9000/ipdata/${ipaddress}`;
const res = await fetch(api_url, {
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
});
const json = await res.json();
console.log(json);
document.getElementById('city').textContent = json.city;
document.getElementById('state').textContent = json.region_code;
document.getElementById('zip').textContent = json.zip;
}
render() {
return (
<div className="App">
<h1>IP Search</h1>
<input type="text"></input>
<button onClick={this.callAPI}>Search IP</button>
<p>
<span id="city" /> <span id="state" /> <span id="zip" />
</p>
</div>
);
}
}
export default App;
There are two issues:
To get the input value, use a controlled component: put the input value into state and add a change handler.
To set the city, state, zip sections, don't use vanilla DOM methods (which should be avoided in React in 95% of situations) - instead, put the response into state.
class App extends React.Component {
constructor(props) {
super(props);
this.state = { apiResponse: '', inputValue: '', result: {} };
}
async callAPI() {
try {
const api_url = `http://localhost:9000/ipdata/${this.state.inputValue}`;
const res = await fetch(api_url, {
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
});
const result = await res.json();
this.setState({ result });
} catch (error) {
// handle errors - don't forget this part
}
}
render() {
return (
<div className="App">
<h1>IP Search</h1>
<input
type="text"
value={this.state.inputValue}
onChange={e => this.setState({ inputValue: e.target.value })}
/>
<button onClick={this.callAPI}>Search IP</button>
<p>
<span>{this.state.result.city}</span>
<span>{this.state.result.state}</span>
<span>{this.state.result.zip}</span>
</p>
</div>
);
}
}
you can store the value of input field inside state and use it directly inside async call.
Plus you need a onchange handler as every time you update input text, state should know the updted value.
import React from 'react';
import './App.css';
class App extends React.Component {
constructor(props) {
super(props);
// HERE.........................
this.state = { apiResponse: '', text : null };
}
// HERE ...........................
handleChnage = (e) => this.setState({text : e.target.value})
async callAPI() {
// Checking the input value and pass to api..................
console.log(this.state.text)
const ipaddress = '8.8.8.8';
const api_url = `http://localhost:9000/ipdata/${ipaddress}`;
const res = await fetch(api_url, {
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
});
const json = await res.json();
console.log(json);
// Don't use it..............use state to pass the data
document.getElementById('city').textContent = json.city;
document.getElementById('state').textContent = json.region_code;
document.getElementById('zip').textContent = json.zip;
}
render() {
// Here on Input element .................
return (
<div className="App">
<h1>IP Search</h1>
<input type="text" value={this.state.text} onChange={this.handleChange}></input>
<button onClick={this.callAPI}>Search IP</button>
<p>
<span id="city" /> <span id="state" /> <span id="zip" />
</p>
</div>
);
}
}
export default App;
Note - don't use imperative methods like getElementById and others in React.
Please avoid using DOM methods in Reactjs, here is an example of what you might want to do with your application.
`
import React,{useState} from 'react';
function App(){
const [apiRes,setApiRes]= useState('');
const [loading,setLoadng]= useState(false);
const callAPI= async()=>{
// supose this is your api response in json
const hello={
city:"city1",
region_code:"region#123",
zip:"00000"
}
// loading while city and zip are not available
setLoadng(true)
await setTimeout(()=>{setApiRes(hello)},5000)
}
return (
<div className="App">
<h1>IP Search</h1>
<input type="text"></input>
<button onClick={callAPI}>Search IP</button>
{!apiRes && loading && <p>loading count till 5...</p>}
<p>
{apiRes &&
(
<>
<span> {apiRes.city}</span>
<span> {apiRes.region_code}</span>
<span> {apiRes.zip}</span>
</>
)}
</p>
</div>
);
}
export default App;
`
link to sandbox: [sandbox]: https://codesandbox.io/s/priceless-mclaren-y7d7f?file=/src/App.js/ "click here to run above code"

I'm having trouble updating state with response from Google Books API

GitHub Repo - With state branch
Hey everyone! I'm currently learning React, state management, and making API requests within react. I was able to fetch and receive a status of 200 based on my client-side application. I'm simply just trying to use the Google Books API to display search results based on filters, just to make sure I understand how an application like this would work.
I ran into trouble with a CORS error. To get around this error I just updated the request mode to no-cors. I think that might be part of my problem though because when I view the component tree using DevTools, the state is not updating with the newly received data and I'm getting an error message "Failed to fetch"' even though the network tab displays a 200 status code.
Any help on how to receive and display fetched data from a server when using state? Any help would be appreciated.
I've included a link to my repo as well as the following code snippets:
Parent Component - App.js
import React, { Component } from "react";
import "../Styles/App.css";
import SiteHeader from "./SiteHeader";
import Form from "./Form";
import BookList from "./BookList";
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
books: [],
searchInput: "",
printFilter: "",
bookFilter: "",
};
}
handleUpdateBooks(data) {
this.setState({
books: data,
});
}
render() {
return (
<div className="App">
<SiteHeader />
<Form updateBooks={(data) => this.handleUpdateBooks(data)} />
<BookList books={this.state.books} />
</div>
);
}
}
Child Component - Form.js
import React, { Component } from "react";
import "../Styles/Form.css";
// UNABLE TO RECEIVE PROPER RESPONSE FROM API. RECEIVING 200 STATUS BUT STATE IS NOT BEING UPDATED WITH THE DATA OBJECT
export default class Form extends Component {
// add a constructor to initialize state for controlled form component
constructor(props) {
super(props);
this.state = {
search: "",
printType: "all",
bookType: "",
};
}
// write methods to update the state when each of the input values are changed
searchChanged(search) {
this.setState({
search,
});
}
printTypeChanged(printType) {
this.setState({
printType,
});
}
bookTypeChanged(bookType) {
this.setState({
bookType,
});
}
formatQueryParams(parameters) {
const queryItems = Object.keys(parameters).map(
(key) => `${key}=${parameters[key]}`
);
return queryItems.join("&");
}
handleSubmit(e) {
e.preventDefault();
// create object of search terms and filters
const BASE_URL = "https://www.googleapis.com/books/v1/volumes";
const parameters = (({ search, printType, bookType, key }) => ({
q: search,
printType,
filter: bookType,
key: "AIzaSyDcxqxraM3gEciVrsqWwQrpAlv5akq_dlk",
}))(this.state);
const queryString = this.formatQueryParams(parameters);
const FETCH_URL = BASE_URL + "?" + queryString;
console.log(FETCH_URL);
// write a method to format the query parameters into correct syntax
this.formatQueryParams(parameters);
fetch(FETCH_URL, {
mode: "no-cors",
})
.then((res) => {
if (!res.ok) {
console.log(res);
throw new Error("Something went wrong, please try again later");
}
return res;
})
.then((res) => res.json())
.then((data) => {
this.props.updateBooks(data);
})
.catch((err) => {
this.setState({
error: err.message,
});
});
}
render() {
return (
<div className="Form">
<form onSubmit={(e) => this.handleSubmit(e)}>
<div className="Form_search">
<label htmlFor="search">
<strong>Search: </strong>
</label>
<input
type="text"
placeholder="Enter book title"
name="search"
id="search"
value={this.state.search}
onChange={(e) => this.searchChanged(e.target.value)}
required
/>
<button type="submit">
<strong>Get Books!</strong>
</button>
</div>
<div className="Form_filters">
<div className="Form_print">
<label htmlFor="print-type">
<strong>Print Type: </strong>
</label>
<select
name="print-type"
id="print-type"
value={this.state.printType}
onChange={(e) => this.printTypeChanged(e.target.value)}
>
<option value="all" selected>
All
</option>
<option value="books">Books</option>
<option value="magazines">Magazines</option>
</select>
</div>
<div className="Form_book">
<label htmlFor="book-type">
<strong>Book Type: </strong>
</label>
<select
name="book-type"
id="book-type"
value={this.state.bookType}
onChange={(e) => this.bookTypeChanged(e.target.value)}
>
<option value="" selected>
No Filter
</option>
<option value="partial">Partial</option>
<option value="full">Full</option>
<option value="ebooks">eBooks</option>
<option value="free-ebooks">Free eBooks</option>
<option value="paid-ebooks">Paid eBooks</option>
</select>
</div>
</div>
</form>
</div>
);
}
}

Password show/hide using Eye/EyeSlash in React

I am trying to implement eye/eyeslash in on my Register form in React.
This is a function that's is responsible for changing visibility type and eye icon changing.
import React, { useState } from "react";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
export const usePasswordToggle = () => {
const [visible, setVisibility] = useState();
const Icon = <FontAwesomeIcon icon={visible ? "eye-slash" : "eye"} />;
const InputType = visible ? "text" : "password";
return [InputType, Icon];
};
I am trying to implement it in component responsible for registering.
import React, { Component, createRef } from "react";
import { usePasswordToggle } from "./usePasswordToggle";
class Register1 extends React.Component {
EmailR = createRef();
UsernameR = createRef();
PasswordR = createRef();
PasswordConfirmR = createRef();
constructor(props) {
super();
this.state = {
message: "",
password: "",
confirmPassword: "",
};
}
handleSubmit = (event) => {
// alert(this.PasswordR.current.value);
// alert(this.PasswordConfirmR.current.value);
if (this.PasswordR.current.value !== this.PasswordConfirmR.current.value) {
alert("The passwords doesn't match");
return false; // The form won't submit
} else {
alert("The passwords do match");
return true; // The form will submit
}
};
onCreateAccount = () => {
let loginInfo = {
Username: this.UsernameR.current.value,
Email: this.EmailR.current.value,
Password: this.PasswordR.current.value,
};
fetch("http://localhost:5000/api/authenticate/register", {
method: "POST",
headers: { "Content-type": "application/json" },
body: JSON.stringify(loginInfo),
})
.then((r) => r.json())
.then((res) => {
if (res) {
this.setState({
message:
"New Account is Created Successfully. Check your email to verify Account.",
});
}
});
};
render() {
return (
<div>
<h2 className="FormDescription">
{" "}
Please enter Account details for registration
</h2>
<div className="Form">
<p>
<label>
Email: <input type="text" ref={this.EmailR} />
</label>
</p>
<p>
<label>
Username: <input type="text" ref={this.UsernameR} />
</label>
</p>
<div>
<label>
Password:{" "}
<input type={usePasswordToggle.InputType} ref={this.PasswordR} />
</label>
<span className="password-toogle-icon">
{usePasswordToggle.Icon}
</span>
</div>
<p>
<label>
ReenterPassword:{" "}
<input type="password" ref={this.PasswordConfirmR} />{" "}
</label>
</p>
<button onClick={this.handleSubmit}> Create </button>
<p>{this.state.message}</p>
</div>
</div>
);
}
}
export default Register1;
My password is always visible, and eye icon is even not visible on the form (it should be inside my input field, but it is not).
Focus on this code snippet:
<div>
<label>
Password: <input type={usePasswordToggle.InputType} ref={this.PasswordR} />
</label>
<span className="password-toogle-icon">{usePasswordToggle.Icon}</span>
</div>
Any suggestion what is the problem?
Change this
const [visible, setVisibility] = useState();
to this
const [visible, setVisible] = useState(true);
as the official documentation here
First, add a default value to your useState, either true or false depending on which icon you want to render first.
Then, you should add a onClick method to your icon which will toggle the visibility state. You're setting the icon based on visible value, but you never toggle the value.
onClick={() => setVisibility(!visible)}
UPDATE
You also need to execute your Hook inside your main component (because yes, you wrote what React call a Hook), like so :
const [inputType, icon] = usePasswordToggle();
But doing so, you'll get an error from React that say you cannot use a Hook within a class component due to how they work.
Basically you need to change your Register1 component to be a functional component, and not a class anymore. Look here for a quick overview on how to : https://reactjs.org/docs/components-and-props.html

How do I join the file url field (Firestore storage) with the other fields in Firestore? (react)

I've trying to:
Upload image to firebase storage
create field 'fileUrl' in Firestore under the 'articles' document
reference the doc in Firestore so that each image knows to which article it is assigned.
I managed to do all of that in a way but the issue is that when I submit the form with a title, content and image, it creates a new document in Firestore with just a the image ('fileUrl'), instead of creating a document that includes the rest of the form's data.
I know that this is probably because in my UploadFile.js I'm creating a new document but how do I join the Field created in UploadFile.js with the fields in AddArticle.js?
I'm also getting ''Unhandled Rejection (TypeError): Cannot read property 'state' of undefined' for this line:
firebase.firestore().collection('articles').doc(this.state.documentId).update({
fileUrl: fileUrl
})
UploadFile.js
import React from "react";
import firebase from '../Firebase';
function UploadFile() {
const [fileUrl, setFileUrl] = React.useState(null);
const onFileChange = async (e) => {
const file = e.target.files[0]
const storageRef = firebase.storage().ref()
const fileRef = storageRef.child(file.name)
await fileRef.put(file);
setFileUrl(await fileRef.getDownloadURL().then(fileUrl => {
firebase.firestore().collection('articles').doc(this.state.documentId).update({
fileUrl: fileUrl
})
.then(() => {
setFileUrl('')
})
} ));
};
return (
<>
<input type="file" onChange={onFileChange} />
<div>
<img width="100" height="100" src={fileUrl} alt=''/>
</div>
</>
);
}
export default UploadFile;
AddArticle.js
import React, { Component } from 'react';
import firebase from '../Firebase';
import UploadFile from '../components/UploadFile';
class AddArticle extends Component {
constructor() {
super();
this.ref = firebase.firestore().collection('articles');
this.state = {
title: '',
content: '',
fileUrl: ''
};
}
onChange = (e) => {
const state = this.state
state[e.target.name] = e.target.value;
this.setState(state);
}
onSubmit = (e) => {
e.preventDefault();
const { title, content, fileUrl } = this.state;
this.ref.add({
title,
content,
fileUrl
}).then((docRef) => {
this.setState({
title: '',
content: '',
fileUrl: '',
documentId: docRef.id
});
this.props.history.push("/")
})
.catch((error) => {
console.error("Error adding document: ", error);
});
}
render() {
const { title, content, fileUrl } = this.state;
return (
<div className="container">
<br></br><br></br><br></br>
<div className="panel panel-default">
<div className="panel-heading">
<h3 className="panel-title text-center">
Create a new article
</h3>
</div>
<br></br><br></br>
<div className="panel-body">
<form onSubmit={this.onSubmit}>
<div className="form-group">
<label for="title">Title:</label>
<input type="text" className="form-control" name="title" value={title} onChange={this.onChange} placeholder="Title" />
</div>
<div className="form-group">
<label for="content">Content:</label>
<textArea className="form-control" name="content" onChange={this.onChange} placeholder="Content" cols="80" rows="20">{content}</textArea>
</div>
{/* <input type="file" onChange={this.onFileChange} /> */}
<UploadFile onChange={this.onChange} value={fileUrl}/>
<button type="submit" className="btn btn-success">Submit</button>
</form>
</div>
</div>
</div>
);
}
}
export default AddArticle;
Each time you call firebase.firestore().collection('articles').add(...), you add a new document to the database. Since you call that in both AddArticle and in UploadFile, your image URL and the other fields indeed will end up in separate documents.
The way you're handling the file upload is a bit unusual to me, as I'd normally expect that upload to happen when the other fields are also submitted. Right now, it's not clear to me what the image belong to, as J.A.Hernández also commented.
Either way: you'll need to remove one of the firebase.firestore().collection('articles').add(...) calls and instead pass the document ID into that call. Say that you first add the text fields and then upload the image, the UploadFile can then update the document with:
firebase.firestore().collection('articles').doc(documentId).update({
fileUrl: fileUrl
})
One way to pass the document ID from the to is by keeping it in the state:
this.ref.add({
title,
content,
fileUrl
}).then((docRef) => {
this.setState({
title: '',
content: '',
fileUrl: '',
documentId: docRef.id
});
this.props.history.push("/")
})
And then:
firebase.firestore().collection('articles').doc(this.state.documentId).update({
fileUrl: fileUrl
})

Resources