Upload avatar to firebase with Antd in React - reactjs

How can I use Upload component in Ant Design to upload avatar to Firebase?
Here is what I used and tried in React:
// Modal hook
const [modal1Open, setModal1Open] = useState(false);
// Upload new avatar
const [loading, setLoading] = useState(false);
const [imageUrl, setImageUrl] = useState();
const handleAvatarChange = (info) => {
if (info.file.status === 'uploading') {
setLoading(true);
return;
}
if (info.file.status === 'done') {
// Get this url from response in real world.
getBase64(info.file.originFileObj, (url) => {
setLoading(false);
setImageUrl(url);
});
}
};
const uploadAvatar2fb = async () => {
console.log('new avatar url: ', imageUrl)
try {
await addAvatarUrl2UserDb(location.state.userEmail, imageUrl)
} catch (error) {
console.log(error.message)
}
}
const uploadButton = (
<div>
{loading ? <LoadingOutlined /> : <PlusOutlined />}
<div
style={{
marginTop: 8,
}}
>
Upload
</div>
</div>
);
return (
{/* change avatar */}
<Button type="primary" onClick={() => setModal1Open(true)}>Change Avatar</Button>
<Modal
title="Upload your new avatar here"
centered
open={modal1Open}
onOk={() => setModal1Open(false)}
onCancel={() => setModal1Open(false)} >
<p>Click below to upload...</p>
<Upload
name="avatar"
listType="picture-card"
className="avatar-uploader"
showUploadList={false}
// upload address
action='./images/'
// check format jpg/png check size < 2mb
beforeUpload={beforeUpload}
// set new upload url
onChange={handleAvatarChange} >
{imageUrl ? (
<img
src={imageUrl}
alt="avatar"
style={{
width: '100%',
}}
/>
) : (
uploadButton
)}
</Upload>
</Modal>
)
How do I handle with the action='' in that <Upload ..> tag?
I wrote this trying to make the uploading to firebase
const uploadAvatar2fb = async () => {
console.log('new avatar url: ', imageUrl)
try {
await addAvatarUrl2UserDb(location.state.userEmail, imageUrl)
} catch (error) {
console.log(error.message)
}
}
which called the function in firebase.js
// save avart to users
export const addAvatarUrl2UserDb = async (email, url) => {
const userDocRef = doc(db, 'users', email)
try {
await setDoc(userDocRef, {
avatar: url
})
} catch (error) {
console.log("avatar url upload error:", error.message)
}
}
And when I tried to upload after start npm, I got the error like POST http:localhost:3000/{url of the action} 404 not found. It shows that the address I wrote in <Upload action='...' /> is not found.
How could I solve this and make the uploading works?

Related

Firebase Not Refreshing Image (React)

I am using firebase with react to store images so that the user can upload them there and then have them be displayed and I manage to display the uploaded image but when I refresh the page. The image gets removed, my function userProfileImage() does not seem to update the image, how can I keep the image?
const [image, setImage] = useState(null);
const [url, setUrl] = useState("");
const [progress, setProgress] = useState(0);
const handleChange = e => {
if (e.target.files[0]) {
setImage(e.target.files[0]);
}
};
const imagesListRef = ref(storage, "images/");
useEffect(() => {
list(imagesListRef).then((response) => {
response.items.forEach((item) => {
getDownloadURL(item).then((url) => {
setUrl(url);
console.log(url);
});
});
});
}, []);
const handleUpload = () => {
const uploadTask =
storage.ref(`images/${image.name}`).put(image);
uploadTask.on(
"state_changed",
snapshot => {
const progress = Math.round(
(snapshot.bytesTransferred / snapshot.totalBytes) * 100
);
setProgress(progress);
},
error => {
console.log(error);
},
() => {
storage
.ref("images")
.child(image.name)
.getDownloadURL()
.then(url => {
setUrl(url);
});
}
);
userProfileImage();
};
return (
<div className='App'>
<Header/>
<br />
<br />
{progress > 0 && progress < 100 ?
<progress value={progress} max="100" /> : <div></div>
}
<div class="proPic">
<Avatar src={url} sx={{ width: 250, height: 250 }} />
<input type="file" onChange={handleChange} />
<button onClick={handleUpload}>Submit</button>
</div>
</div>
)
}
When you are refreshing, all the components will also be refreshed. As a result all the states will be reinitialized. So when you refresh, url will be null again. So you should retrieve the image when the component is refreshed. You can retrive the uploaded image inside a useEffect hook.
useEffect(()=>{
//retrieve image
},[]}

React class component convert to functional component (Antdesign)

I'm new to React and I want to use Antdesign for image upload, but the component class component given in ant design's site. How can I convert the code below into a functional component?
in what way can i do this? Or is there any online tool that can do this?
here is my code :
function getBase64(img, callback) {
const reader = new FileReader();
reader.addEventListener('load', () => callback(reader.result));
reader.readAsDataURL(img);
}
function beforeUpload(file) {
const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';
if (!isJpgOrPng) {
message.error('You can only upload JPG/PNG file!');
}
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isLt2M) {
message.error('Image must smaller than 2MB!');
}
return isJpgOrPng && isLt2M;
}
class Avatar extends React.Component {
state = {
loading: false,
};
handleChange = info => {
if (info.file.status === 'uploading') {
this.setState({ loading: true });
return;
}
if (info.file.status === 'done') {
// Get this url from response in real world.
getBase64(info.file.originFileObj, imageUrl =>
this.setState({
imageUrl,
loading: false,
}),
);
}
};
render() {
const { loading, imageUrl } = this.state;
const uploadButton = (
<div>
{loading ? <LoadingOutlined /> : <PlusOutlined />}
<div style={{ marginTop: 8 }}>Upload</div>
</div>
);
return (
<Upload
name="avatar"
listType="picture-card"
className="avatar-uploader"
showUploadList={false}
action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
beforeUpload={beforeUpload}
onChange={this.handleChange}
>
{imageUrl ? <img src={imageUrl} alt="avatar" style={{ width: '100%' }} /> : uploadButton}
</Upload>
);
}
}
ReactDOM.render(<Avatar />, mountNode);
You will need to replace your class with a function that returns a JSX element.
You'll need to refactor your state to use the useState hook: https://reactjs.org/docs/hooks-state.html
DigitalOcean has a nice guide here: https://www.digitalocean.com/community/tutorials/five-ways-to-convert-react-class-components-to-functional-components-with-react-hooks
It will end up looking something like the following:
function getBase64(img, callback) {
const reader = new FileReader();
reader.addEventListener("load", () => callback(reader.result));
reader.readAsDataURL(img);
}
function beforeUpload(file) {
const isJpgOrPng = file.type === "image/jpeg" || file.type === "image/png";
if (!isJpgOrPng) {
message.error("You can only upload JPG/PNG file!");
}
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isLt2M) {
message.error("Image must smaller than 2MB!");
}
return isJpgOrPng && isLt2M;
}
function Avatar() {
const [imageUrl, setImageUrl] = useState(null);
const [loading, setLoading] = useState(false);
const handleChange = (info) => {
if (info.file.status === "uploading") {
setLoading(true)
return;
}
if (info.file.status === "done") {
// Get this url from response in real world.
getBase64(info.file.originFileObj, (imageUrl) => {
setImageUrl(imageUrl);
setLoading(false);
});
}
};
const uploadButton = (
<div>
{loading ? <LoadingOutlined /> : <PlusOutlined />}
<div style={{ marginTop: 8 }}>Upload</div>
</div>
);
return (
<Upload
name="avatar"
listType="picture-card"
className="avatar-uploader"
showUploadList={false}
action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
beforeUpload={beforeUpload}
onChange={handleChange}
>
{imageUrl ? (
<img src={imageUrl} alt="avatar" style={{ width: "100%" }} />
) : (
uploadButton
)}
</Upload>
);
};

Playing audio file from Firebase Storage works locally, but not on deployed appliation

I've been working on a prototype of AAC application for a project. Basically you hit a button, it says a word (like a sound board). I'm using firebase to host it and firebase storage to store the files. It works locally off my computer, but when I go to the deployed application, the button presses do nothing. Is there something I'm missing here? The storage addresses are redacted and replaced with "BUCKET". The url has the bucket address as well as the token attached to them. Any help would be greatly appreciated.
import React, { useState } from "react"
import { Card, Button, Alert, ButtonGroup } from "react-bootstrap"
import { useAuth } from "../contexts/AuthContext"
import { Link, useHistory } from "react-router-dom"
export default function Dashboard() {
const [error, setError] = useState("")
const { currentUser, logout } = useAuth()
const history = useHistory()
let bpYes = new Audio("BUCKET")
const pYes = () => {
bpYes.play()
}
let bpNo = new Audio("BUCKET")
const pNo = () => {
bpNo.play()
}
let bpPlease = new Audio("BUCKET")
const pPlease = () => {
bpPlease.play()
}
let bpMore = new Audio("BUCKET")
const pMore = () => {
bpMore.play()
}
let bpEat = new Audio("BUCKET")
const pEat = () => {
bpEat.play()
}
let bpDrink = new Audio("BUCKET")
const pDrink = () => {
bpDrink.play()
}
let bpPlay = new Audio("BUCKET")
const pPlay = () => {
bpPlay.play()
}
let bpPotty = new Audio("BUCKET")
const pPotty = () => {
bpPotty.play()
}
async function handleLogout() {
setError("")
try {
await logout()
history.push("/login")
} catch {
setError("Failed to log out")
}
}
return (
<>
<div className="text-center">
<h1>AAC PROTOTYPE</h1>
<h2>Welcome!</h2>
</div>
<Card>
<Card.Body>
<h2 className="text-center mb-4">Profile</h2>
{error && <Alert variant="danger">{error}</Alert>}
<strong>Email:</strong> {currentUser.email}
<Link to="/update-profile" className="btn btn-primary w-100 mt-3">
Update Profile
</Link>
</Card.Body>
</Card>
<Card>
<Card.Body>
<ButtonGroup aria-label="Basic example" class="col-md-12 text-center">
<Button size="lg" variant="outline-primary" onClick={pYes}>Yes</Button>
<Button size="lg" variant="outline-primary" onClick={pNo}>No</Button>
<Button size="lg" variant="outline-primary" onClick={pMore}>More</Button>
<Button size="lg" variant="outline-primary" onClick={pPlease}>Please</Button>
</ButtonGroup>
<ButtonGroup aria-label="Basic example" class="col-md-12 text-center">
<Button size="lg" variant="outline-primary"onClick={pEat}>Eat</Button>
<Button size="lg" variant="outline-primary"onClick={pDrink}>Drink</Button>
<Button size="lg" variant="outline-primary" onClick={pPlay}>Play</Button>
<Button size="lg" variant="outline-primary" onClick={pPotty}>Potty</Button>
</ButtonGroup>
</Card.Body>
</Card>
<div className="w-100 text-center mt-2">
<Button variant="link" onClick={handleLogout}>
Log Out
</Button>
</div>
</>
)
}
Firebase doesn't support streaming audio files from storage, it works closer to a file download so a work around is needed.
Attached below is a common react script to play audio blobs from firebase storage and another for uploading.
Download and play script
const downloadAudio = async () => {
const uri = await firebase
.storage()
.ref("nameOfTheFile.filetype")
.getDownloadURL();
console.log("uri:", uri);
// The rest of this plays the audio
const soundObject = new Audio.Sound();
try {
await soundObject.loadAsync({ uri });
await soundObject.playAsync();
} catch (error) {
console.log("error:", error);
}
};
Upload as a data blob script
const uploadAudio = async () => {
const uri = recording.getURI();
try {
const blob = await new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.onload = () => {
try {
resolve(xhr.response);
} catch (error) {
console.log("error:", error);
}
};
xhr.onerror = (e) => {
console.log(e);
reject(new TypeError("Network request failed"));
};
xhr.responseType = "blob";
xhr.open("GET", uri, true);
xhr.send(null);
});
if (blob != null) {
const uriParts = uri.split(".");
const fileType = uriParts[uriParts.length - 1];
firebase
.storage()
.ref()
.child(`nameOfTheFile.${fileType}`)
.put(blob, {
contentType: `audio/${fileType}`,
})
.then(() => {
console.log("Sent!");
})
.catch((e) => console.log("error:", e));
} else {
console.log("erroor with blob");
}
} catch (error) {
console.log("error:", error);
}
};

Cant send photo from React frontend to express backend after build (create-react-app)

Ok so i have an MERN app (CRA) and photo upload. Everything works great but when i will do npm run build i cant send photo (over 1MB) to backend. I've used postman to test and again worked great :). In development mode its working. Something is blocking requests from frontend when i want to upload photos. I've checked logs with morgan and it shows that this request with large photo upload didnt happen. Im using axios to communicate with backend and express file upload but backend works well. I dont know what is blocking my photos. Also express file upload in debuging mode is telling that "Request is not eligible for upload!"
action in redux:
export const addGalleryPhoto = (file) => async (dispatch) => {
const config = {
headers: {
"Content-Type": "multipart/form-data",
},
};
try {
await axios.put(`/api/v1/config/uploadGalleryPhoto`, file, config);
dispatch(getPhotos());
} catch (err) {
const errors = err.response.data.error.split(",");
if (errors) {
errors.forEach((error) => dispatch(setAlert(error, "error")));
}
dispatch({ LOAD_PHOTOS_FAILED });
}
};
controller in node:
exports.uploadGalleryPhoto = asyncHandler(async (req, res, next) => {
if (!req.files) {
return next(new ErrorResponse(`Dodaj plik`, 400));
}
const file = req.files.file;
// Make sure the image is a photo
if (!file.mimetype.startsWith("image")) {
return next(new ErrorResponse(`Możesz wysłać tylko zdjęcia`, 400));
}
if (file.size > process.env.MAX_FILE_UPLOAD) {
return next(
new ErrorResponse(
`Zbyt duże zdjęcie. Maksymalny rozmiar pliku: ${Math.round(
process.env.MAX_FILE_UPLOAD / 1024
)} MB`,
400
)
);
}
file.name = `photo_${uuid()}${path.parse(file.name).ext}`;
file.mv(`${process.env.FILE_UPLOAD_PATH}/${file.name}`, async (err) => {
if (err) {
console.error(err);
return next(new ErrorResponse(`Problem with file upload`, 500));
}
const config = await Config.find();
await Config.findByIdAndUpdate(config[0].id, {
galleryPhotos: [...config[0].galleryPhotos, file.name],
});
res.status(200).json({
success: true,
data: file.name,
});
});
});
Gallery component:
import React, { useEffect, useState, useRef, Fragment } from "react";
import PropTypes from "prop-types";
import { Card, Modal, Button } from "antd";
import { DeleteOutlined } from "#ant-design/icons";
import Spinner from "./../layout/Spinner";
import { connect } from "react-redux";
import {
addGalleryPhoto,
getPhotos,
deletePhoto
} from "./../../store/actions/gallery";
const AdminGallery = ({ photos, addGalleryPhoto, getPhotos, deletePhoto }) => {
useEffect(() => {
getPhotos();
}, [getPhotos]);
const fileInput = useRef();
const [value, setValue] = useState("");
const [formData, setFormData] = useState({
deleteVisible: false,
photo: null
});
const onSubmit = e => {
e.preventDefault();
const formData = new FormData();
formData.append("file", value);
addGalleryPhoto(formData);
setValue("");
};
if (photos.loading || photos.photos.null) {
return <Spinner />;
} else {
return (
<Fragment>
<div className="admin-gallery">
<div>
<input
type="file"
style={{ display: "none" }}
ref={fileInput}
onChange={e => setValue(e.target.files[0])}
/>
{value === "" ? (
<button
type="button"
className="btn btn--primary"
onClick={e => fileInput.current.click()}
>
Dodaj plik
</button>
) : (
<button
type="button"
className="btn btn--primary"
onClick={e => onSubmit(e)}
>
Wyślij zdjęcie
</button>
)}
<div>
{photos.photos.length === 0 ? (
<div className="no-content">Brak Zdjęć</div>
) : (
<div className="admin-gallery__photo-wrapper">
{photos.photos.map(item => {
return (
<Card
style={{ padding: "0 !important" }}
key={item}
actions={[
<DeleteOutlined
onClick={() =>
setFormData({
...formData,
deleteVisible: true,
photo: item
})
}
/>
]}
>
<img
className="admin-gallery__photo"
src={`${process.env.PUBLIC_URL}/uploads/${item}`}
alt="photo"
/>
</Card>
);
})}
</div>
)}
</div>
</div>
</div>
{formData.deleteVisible && formData.photo !== null ? (
<Modal
visible={formData.deleteVisible}
title="Kasowanie zdjęcia"
onOk={() => {
deletePhoto(formData.photo);
setFormData({ ...formData, deleteVisible: false, photo: null });
}}
onCancel={() =>
setFormData({ ...formData, deleteVisible: false, photo: null })
}
footer={[
<Button
key="back"
onClick={() =>
setFormData({
...formData,
deleteVisible: false,
photo: null
})
}
>
Zamknij
</Button>,
<Button
key="accept"
onClick={() => {
deletePhoto(formData.photo);
setFormData({
...formData,
deleteVisible: false,
photo: null
});
}}
>
Skasuj
</Button>
]}
>
<p>Na pewno chcesz skasować to zdjęcie?</p>
</Modal>
) : (
""
)}
</Fragment>
);
}
};
AdminGallery.propTypes = {
photos: PropTypes.object.isRequired,
getPhotos: PropTypes.func.isRequired,
addGalleryPhoto: PropTypes.func.isRequired,
deletePhoto: PropTypes.func.isRequired
};
const mapStateToProps = state => ({
photos: state.photos
});
export default connect(mapStateToProps, {
addGalleryPhoto,
getPhotos,
deletePhoto
})(AdminGallery);
Ok guys i've found an issue. I have nginx on my droplet and default file size to upload in nginx is set to 1mb. This line in nginx conf will do the job client_max_body_size 100M; Of course 100M is for example.

How to upload files using React?

so currently I am trying to upload a file, and save it on the Google Clouds. But I am stuck at how to get the input file using React-Redux.
Basically, my back-end part already finish, I tested it using HTTPie with this command
HTTP localhost:8000/v1/... #(file_path)
and it works perfectly fine.
Now after I use input tag:
<input type="file" onChange="">
I do not know how to get the file_path that are the user choose.
And I dont even know whether I can get the uploaded file using this method.
Thank you
You cant handle onChange file by method <input type="file" onChange={this.handleChangeFile.bind(this)}>
handleChangeFile(event) {
const file = event.target.files[0];
let formData = new FormData();
formData.append('file', file);
//Make a request to server and send formData
}
First : Create a component file name 'FileUpload.js'
import React, { Fragment, useState, useEffect } from 'react'
function FileUpload (props) {
useEffect(() => {
clearFileUpload()
}, [])
const [fileName, setFileName] = useState('')
const [fileSize, setFileSize] = useState('')
const [fileSizeKB, setFileSizeKB] = useState('')
const [fileType, setFileType] = useState('')
const [src, setSrc] = useState('')
const clearFileUpload = () => {
setFileName('')
setFileSize('')
setFileType('')
setSrc('')
props.dataChanger('')
}
const onPickFile = (e) => {
e.preventDefault();
clearFileUpload()
document.getElementById(props?.name).click()
}
const onFilePicked = (e) => {
let files = e.target.files;
let file_name = files[0].name;
let file_size = getFileSize(files[0].size);
let file_size_kb = getFileSizeKB(files[0].size);
let file_type = getFileType(files[0]).toLowerCase();
setFileName(file_name)
setFileSize(file_size)
setFileSizeKB(file_size_kb)
setFileType(file_type)
if (props?.max_file_size_in_kb && file_size_kb > props?.max_file_size_in_kb)
{
alert('Maximum allowed file size = '+props?.max_file_size_in_kb+ ' kb')
clearFileUpload()
return false;
}
if (props?.allowed_extensions && !arrToLowerCase(props?.allowed_extensions).includes(file_type))
{
clearFileUpload()
alert('Allowed file type = '+props?.allowed_extensions)
return false;
}
let fileReader = new FileReader();
fileReader.addEventListener('load', ()=>{
// console.log(fileReader.result);
props.dataChanger(fileReader.result)
setSrc(fileReader.result)
})
fileReader.readAsDataURL(files[0])
}
const getFileSize = (file_size) =>
{
if ( (file_size/1024) >= 1024 )
{
file_size= parseInt((file_size/1024)/1024) + ' MB';
}
else{
file_size=parseInt(file_size/1024) + ' KB';
}
return file_size;
}
const getFileSizeKB = (file_size) =>
{
file_size=parseInt(file_size/1024);
return file_size;
}
const getFileType = (file) =>
{
return file?.type.split('/').pop();
}
const arrToLowerCase = (arr=[]) => {
return arr.map(str => str.toLowerCase());
}
return (
<Fragment>
<button className="btn btn-primary text-capitalize mr-2 mb-2" onClick={(e) => onPickFile(e)}>{props?.button_title || 'Upload File'}</button>
{(props?.required && fileName?.length<=3 && !src ) ? <label className="label label-danger">Required</label> : ''}
<br />
{fileName ? <label className="label label-primary">{fileName}</label> : ''}
{fileSize ? <label className="label label-info">{fileSize}</label> : ''}
<br />
{/* new upload file */}
{(props?.type=='image' && src && (props?.prev_src)) ? <img src={src} style={{ maxHeight: "150px", maxWidth: "150px" }} alt="" className="mt-2" /> : ''}
{/* previous image */}
{(props?.type=='image' && (props?.prev_src) && !src) ? <img src={props?.prev_src} style={{ maxHeight: "150px", maxWidth: "150px" }} alt="" className="mt-2" /> : ''}
{(props?.type=='image' && src && (props?.prev_src)) ? <button className="btn btn-danger btn-outline-danger pl-1 pr-0 py-0 ml-2" onClick={clearFileUpload} title="Remove file"><i className="icofont icofont-ui-close"></i></button> : ''}
<input className='file d-none' type="file" data-show-upload="true" data-show-caption="true"
id={props?.name}
name={props?.name}
required={props?.required ? true : false}
onChange={(e) => onFilePicked(e)}
/>
</Fragment>
)
}
export default FileUpload
Second : How to use this component in your form
import FileUpload from './FileUpload.js'
<FileUpload
name="thumbImage"
button_title="Thumbnail Image Upload"
max_file_size_in_kb="200"
dataChanger={(value) => dataChangerThumbnail(value)}
type="image"
prev_src={'localhost:8001/'+formData?.thumbImage}
required
allowed_extensions={[ 'jpg', 'jpeg', 'png', 'gif']}
/>
const dataChangerThumbnail = (value) => {
setFormData({...formData, thumbImage: value})
}
const formInitial = {
thumbImage: '',
}
const [formData, setFormData] = useState(formInitial)
Checkout this blog, very well explained upload feature in react with example.
Below is upload code from above blog:
handleUploadedFiles = files => {
var reader = new FileReader();
reader.onload = (e) => {
//Split csv file data by new line so that we can skip first row which is header
let jsonData = reader.result.split('\n');
let data = [];
jsonData.forEach((element, index) => {
if(index) {
//Split csv file data by comma so that we will have column data
const elementRaw = element.split(',');
console.log(element, index);
if(element) {
let param = {
'id' : elementRaw[0],
'name' : elementRaw[1],
'age' : elementRaw[2],
'address' : elementRaw[3]
}
data.push(param);
}
}
});
}
console.log("TCL: Dashboard -> reader.readyState", reader.readyState)
reader.readAsText(files[0]);
}
Complete Example with AXIOS and FORMIK
import "./App.css";
import { useEffect, useState } from "react";
import * as Yup from "yup";
import { Formik, Field, Form, ErrorMessage, useField } from "formik";
import axios from "axios";
function App() {
return (
<Formik
initialValues={{
profile: [],
}}
validationSchema={Yup.object({
profile:Yup.array().min(1,"select at least 1 file")
})}
onSubmit={(values, props) => {
let data = new FormData();
values.profile.forEach((photo, index) => {
data.append(`photo${index}`, values.profile[index]);
});
axios
.post("you_api_for_file_upload", data, {
headers: {
"Content-Type": "multipart/form-data",
},
})
.then((response) => {
console.log(response);
})
.catch((err) => {
console.log(err);
});
}}
>
{(formik) => {
return (
<>
<Form>
<input
id="file"
name="profile"
type="file"
onChange={(event) => {
const files = event.target.files;
let myFiles =Array.from(files);
formik.setFieldValue("profile", myFiles);
}}
multiple
/>
<ErrorMessage name="profile"/>
<button type="submit" disabled={formik.isSubmitting}>
Submit
</button>
</Form>
</>
);
}}
</Formik>
);
}
export default App;
Hope this will help you

Resources