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
Related
I want to know how can I properly upload a file using ReactJS to api using NestJS, so far here is what I have done
in API's swagger, here is the post method for file upload
This is the table from PostgreSQL for that entity
The api is created using this crud typeorm #nestjsx/crud-typeorm
And in my ReactJS code, currently I have this page:
const EditBadge = () => {
const { id } = useParams();
const history = useNavigate();
const [data, setData] = useState({
id: "",
badge_name: "",
badge_description: "",
img_icon: "",
});
const [baseImage, setBaseImage] = useState("");
const { badge_name, badge_description, img_icon } = data;
const onInputChange = (e: any) => {
setData({ ...data, [e.target.name]: e.target.value });
};
const onSubmit = async (e: any) => {
e.preventDefault();
await api.patch(`badges/${id}`, data);
history("/badge");
};
const loadData = async () => {
const result = await api.get(`badges/${id}`);
setData(result.data);
};
useEffect(() => {
loadData();
}, []);
const uploadImage = async (e: any) => {
const file = e.target.files[0];
const base64 = await convertBase64(file);
setBaseImage(String(base64));
};
const convertBase64 = (file: any) => {
return new Promise((resolve, reject) => {
const fileReader = new FileReader();
fileReader.readAsDataURL(file);
fileReader.onload = () => {
resolve(fileReader.result);
};
fileReader.onerror = (error) => {
reject(error);
};
});
};
return (
<Container className="create-badge-container">
<Container className="create-badge-content">
<Row>
<Col>
<div className="img-container text-center">
<Image
className="p-2"
src={baseImage}
alt=""
width={198}
height={219}
/>
</div>
</Col>
</Row>
<Row>
<Col>
<Form className="create-badge-form" onSubmit={(e) => onSubmit(e)}>
<Row className="pt-4">
<Col className="col-4">
<input
type="text"
className="form-control"
placeholder="Badge Name"
aria-label="Badge Name"
name="badge_name"
value={badge_name}
onChange={(e) => onInputChange(e)}
/>
</Col>
<Col className="col-4">
<input
className="form-control"
type="file"
id="file"
accept=".jpeg, .png, .jpg"
name="img_icon"
onChange={(e) => {
uploadImage(e);
}}
/>
</Col>
</Row>
<Row className="pt-4">
<Col className="col-8">
<textarea
className="form-control"
id="exampleFormControlTextarea1"
placeholder="Description"
rows={3}
name="badge_description"
value={badge_description}
onChange={(e) => onInputChange(e)}
></textarea>
</Col>
</Row>
<Row className="pt-5">
<Col className="col-4">
<Button type="submit" className="btn-create">
Update
</Button>
</Col>
</Row>
</Form>
</Col>
</Row>
</Container>
</Container>
);
};
export default EditBadge;
So far this is what I've got base on research, and I got stuck and have no idea how can I post/create this with the uploaded image file
I don't know how you prefer to do things me in my case I used axios and the FormData class
my reactjs code
import React,{ ChangeEvent, useState } from "react";
import axios from "axios";
const UploadFile = () => {
const [file, setFile] = useState<any>();
const onChange = (file: ChangeEvent) => {
const { files } = file.target as HTMLInputElement;
if (files && files.length !== 0) {
setFile(files[0]);
}
}
const handleUpload = async () => {
const formData = new FormData();
formData.append('file', file)
const upload = await axios({
url:"http://localhost:4000/upload",
method:"post",
headers:{
Authorization: `Bearer your token`
},
data:formData
}).then(r => r);
console.log(upload);
}
return (
<div>
<form onSubmit={e => e.preventDefault()}>
<input type="file" onChange={onChange} />
<button onClick={handleUpload}>upload</button>
</form>
</div>
)
}
export default UploadFile
and my nestjs code
import { Controller, Post, UploadedFile, UseGuards, UseInterceptors } from "#nestjs/common";
import { diskStorage } from "multer";
import { randomUUID } from 'crypto';
import Path = require('path');
import { FileInterceptor} from '#nestjs/platform-express';
import { JwtAuthGuard } from "src/auth/guard/jwt.auth.guard";
const storage = {
storage : diskStorage({
destination: 'src/uploads/files',
filename: (req, file, cb) =>{
const filename: string = 'myfile-' + randomUUID();
const extension: string = Path.parse(file.originalname).ext;
cb(null, `${filename}${extension}`)
}
})
}
#Controller('upload')
export class UploadController {
// #UseGuards(JwtAuthGuard) your methode of guard
#Post()
#UseInterceptors(FileInterceptor('file', storage))
uploaiFile(
#UploadedFile() file:any
){
console.log(file)
return file
}
}
I'm making in react a list of episodes that user would like to watch later (similar to todo app) , but after reloading the page data is not keeping in local storage.
I'm new to react, so, please help me to understand the issue.
This is my code
import React, { useState, useEffect } from "react";
import { RiCloseCircleLine } from "react-icons/ri";
import { TiEdit } from "react-icons/ti";
import { MyWatchListForm } from "./MyWatchListForm";
export const MyWatchListItem = ({
watchLists,
completeWatchList,
removeWatchList,
updateWatchList,
}) => {
const [edit, setEdit] = useState({
id: null,
value: "",
});
const submitUpdate = (value) => {
updateWatchList(edit.id, value);
setEdit({
id: null,
value: "",
});
};
useEffect(() => {
const data = localStorage.getItem("my-watchList");
const savedData = JSON.parse(data);
setEdit(savedData);
}, []);
useEffect(() => {
localStorage.setItem("my-watchList", JSON.stringify(watchLists));
});
if (edit.id) {
return <MyWatchListForm edit={edit} onSubmit={submitUpdate} />;
}
return watchLists.map((watchList, index) => (
<div className={watchList.isComplete ? "checked" : ""} key={index}>
<div key={watchList.id} onClick={() => completeWatchList(watchList.id)}>
{watchList.text}
</div>
<div>
<RiCloseCircleLine onClick={() => removeWatchList(watchList.id)} />
<TiEdit
onClick={() => setEdit({ id: watchList.id, value: watchList.text })}
/>
</div>
</div>
));
};
Form that is used to get the data from:
export const MyWatchListForm = (props) => {
const [input, setInput] = useState(props.edit ? props.edit.value : "");
const inputRef = useRef();
useEffect(() => {
inputRef.current.focus();
});
const handleSubmit = (e) => {
e.preventDefault();
props.onSubmit({
id: Math.floor(Math.random() * 10000),
text: input,
});
setInput("");
};
const handleChange = (e) => {
setInput(e.target.value);
};
return (
<form
className="w-full max-w-sm flex items-center border-b border-teal-500 py-2"
onSubmit={handleSubmit}
>
{props.edit ? (
<>
<input
className="appearance-none bg-transparent border-none w-full text-gray-700 mr-3 py-1 px-2 leading-tight focus:outline-none"
type="text"
value={input}
placeholder="Update the episode"
onChange={handleChange}
ref={inputRef}
></input>
<button>Update</button>
</>
) : (
<>
<input
className="appearance-none bg-transparent border-none w-full text-gray-700 mr-3 py-1 px-2 leading-tight focus:outline-none"
type="text"
value={input}
placeholder="Add the episode"
onChange={handleChange}
ref={inputRef}
></input>
<button>Add</button>
</>
)}
</form>
);
};
And the WatchList.js file
import React, { useState } from "react";
import { MyWatchListForm } from "./MyWatchListForm";
import { MyWatchListItem } from "./MyWatchListItem";
export const MyWatchList = () => {
const [watchLists, setWatchLists] = useState([]);
const addWatchList = (watchList) => {
if (!watchList.text || /^\s*$/.test(watchList.text)) {
return;
}
const newWatchList = [watchList, ...watchLists];
setWatchLists(newWatchList);
console.log(...watchLists);
};
const updateWatchList = (watchListId, newValue) => {
if (!newValue.text || /^\s*$/.test(newValue.text)) {
return;
}
setWatchLists((prev) =>
prev.map((item) => (item.id === watchListId ? newValue : item))
);
};
const removeWatchList = (id) => {
const removeArr = [...watchLists].filter(
(watchList) => watchList.id !== id
);
setWatchLists(removeArr);
};
const completeWatchList = (id) => {
const updatedWatchList = watchLists.map((watchList) => {
if (watchList.id === id) {
watchList.isComplete = !watchList.isComplete;
}
return watchList;
});
setWatchLists(updatedWatchList);
};
return (
<div>
<h1>Watch later</h1>
<MyWatchListForm onSubmit={addWatchList} />
<MyWatchListItem
watchLists={watchLists}
completeWatchList={completeWatchList}
removeWatchList={removeWatchList}
updateWatchList={updateWatchList}
/>
</div>
);
};
I fixed the issue by changing the initial state in MyWatchList as below:
export const MyWatchList = () => {
const [watchLists, setWatchLists] = useState(() => {
const data = localStorage.getItem("my-watchList");
return data ? JSON.parse(data) : [];
});
useEffect(() => {
localStorage.setItem("my-watchList", JSON.stringify(watchLists));
}, [watchLists]);
I think the issue with your code is this snippet:
useEffect(() => {
localStorage.setItem("my-watchList", JSON.stringify(watchLists));
});
Every time the state get's updated, this useEffect runs and it replaces the content of localStorage to the values which you are getting in watchlists since you are not providing a dependancy array to the useEffect.
Using the useEffect hook without a dependency array is never a good idea. It basically runs on every render. Actually, goal of the useEffect is to solve this problem. Remove your useEffect and try like this;
const submitUpdate = (value) => {
updateWatchList(edit.id, value);
setEdit({
id: null,
value: "",
});
localStorage.setItem("my-watchList", JSON.stringify(watchLists));
};
Actions/index.js The problem is in the PostArticleAPI() function
import db, { auth, provider, storage } from "../firebase";
import { SET_USER } from "./actionType";
export const setUser = (payload) => ({
type: SET_USER,
user: payload
});
export function signInAPI() {
return (dispatch) => {
auth.signInWithPopup(provider).then((payload) => {
console.log(payload);
dispatch(setUser(payload.user));
}).catch((error) => alert(error.message));
}
}
export function getUserAuth() {
return (dispatch) => {
auth.onAuthStateChanged(async (user) => {
if (user) {
dispatch(setUser(user));
}
})
}
}
export function signOutAPI() {
return (dispatch) => {
auth.signOut().then(() => {
dispatch(setUser(null));
}).catch((error) => {
console.error(error.message);
});
}
}
export function postArticleAPI(payload) {
return (dispatch) => {
if (payload.image != '') {
const upload = storage.ref(`images/${payload.image.name}`).put(payload.image);
upload.on('state_changed', (snapshot) => {
const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
console.log(`Progress: ${progress}%`);
if (snapshot.state === 'RUNNING') {
console.log(`Progress: ${progress}%`);
}
}, (error) => console.log(error.code),
async () => {
const downloadURL = await upload.snapshot.ref.getDownloadURL();
db.collection('articles').add({
actor: {
description: payload.user.email,
title: payload.user.displayName,
date: payload.timestamp,
image: payload.user.photoURL
},
video: payload.video,
shareImg: downloadURL,
comments: 0,
description: payload.description,
})
}
);
}
}
}
PostModal.js
import React, { useState } from 'react'
import styled from 'styled-components';
import ReactPlayer from 'react-player';
import { connect } from 'react-redux';
import { postArticleAPI } from '../actions';
import firebase from 'firebase';
const PostModal = (props) => {
const [editorText, setEditorText] = useState('');
const [shareImage, setShareImage] = useState('');
const [videoLink, setVideoLink] = useState('');
const [assetArea, setAssetArea] = useState('');
const handleChange = (e) => {
const image = e.target.files[0];
if (image === '' || image === undefined) {
alert(`not an image, the file is a ${typeof (image)}`);
return;
}
setShareImage(image);
}
const switchAssetArea = (area) => {
setShareImage("");
setVideoLink("");
setAssetArea(area);
};
const postArticle = (e) => {
console.log("heyy abay");
e.preventDefault();
if (e.target !== e.currentTarget) {
console.log("heyy abay2");
return;
}
const payload = {
image: shareImage,
video: videoLink,
user: props.user,
description: editorText,
timestamp: firebase.firestore.Timestamp.now(),
}
props.postArticle(payload);
reset(e)
}
const reset = (e) => {
setEditorText("");
setShareImage("");
setVideoLink("");
setAssetArea("");
props.handleClick(e);
};
return (
<>
{props.showModal === "open" && (
<Container>
<Content>
<Header>
<h2>Create a post</h2>
<button onClick={(e) => reset(e)}>
<img src="/images/cancel.svg" alt="cancel" />
</button>
</Header>
<SharedContent>
<UserInfo>
{props.user && props.user.photoURL ? (
<img src={props.user.photoURL} alt="" />)
: (<img src="/images/user.svg" alt="" />)}
{/* <img src="/images/user.svg" alt="" /> */}
<span>{props.user.displayName}</span>
</UserInfo>
<Editor>
<textarea value={editorText}
onChange={(e) => setEditorText(e.target.value)}
placeholder='What do you want to talk about?'
autoFocus={true}
/>
{assetArea === "image" ? (
<UploadImage>
<input type="file"
accept='image/gif, image/jpeg, image/png'
name='image'
id='file'
style={{ display: 'none' }}
onChange={handleChange}
/>
<p>
<label htmlFor="file">Select an image to share</label>
</p>
{shareImage && <img
src={URL.createObjectURL(shareImage)} />}
</UploadImage>) : (
assetArea === "media" && (
<>
<input
type="text"
placeholder="Please input a video link"
value={videoLink}
onChange={e => setVideoLink(e.target.value)}
/>
{videoLink && (<ReactPlayer width={"100%"}
url={videoLink} />)}
</>))
}
</Editor>
</SharedContent>
<ShareCreations>
<AttachAssets>
<AssetButton onClick={() => switchAssetArea("image")} >
<img src="/images/gallery.svg" alt="" />
</AssetButton>
<AssetButton onClick={() => switchAssetArea("media")}>
<img src="/images/video.svg" alt="" />
</AssetButton>
</AttachAssets>
<ShareComment>
<AssetButton>
<img src="/images/chat1.svg" alt="" />
Anyone
</AssetButton>
</ShareComment>
<PostButton disabled={!editorText ? true : false}
onClick={(e) => postArticle(e)}>
Post
</PostButton>
</ShareCreations>
</Content>
</Container>)
}
</>
)
};
//I'm hiding CSS
const mapStateToProps = (state) => {
return {
user: state.userState.user,
};
}
const mapDispatchToProps = (dispatch) => ({
postArticle: (payload) => dispatch(postArticleAPI(payload)),
});
export default connect(mapStateToProps, mapDispatchToProps)(PostModal);
Error:
TypeError: firebase__WEBPACK_IMPORTED_MODULE_0_.storage.ref is not a function
This is the error I'm getting and in this line actions/index.js
const upload = storage.ref(`images/${payload.image.name}`).put(payload.image);
It is saying storage.ref is not a function. I am getting this error that is why I'm writing this extra lines
You are probably not exporting properly the storage from ../firebase.
If you share the code from that file we can help you more. Maybe it's also a missmatch with the new Firebase API version (v9) and old one (v8).
I have built a component CreatePost which is used for creating or editing posts,
the problem is if I render this component twice even if I upload a file from the second component they are changed in the first one, why? Here is the code:
import FileUpload from "#components/form/FileUpload";
import { Attachment, Camera, Video, Writing } from "public/static/icons";
import styles from "#styles/components/Post/CreatePost.module.scss";
import { useSelector } from "react-redux";
import { useInput, useToggle } from "hooks";
import { useRef, useState } from "react";
import StyledButton from "#components/buttons/StyledButton";
import Modal from "#components/Modal";
import { post as postType } from "types/Post";
import Removeable from "#components/Removeable";
interface createPostProps {
submitHandler: (...args) => void;
post?: postType;
isEdit?: boolean;
}
const CreatePost: React.FC<createPostProps> = ({ submitHandler, post = null, isEdit = false }) => {
console.log(post);
const maxFiles = 10;
const [showModal, setShowModal, ref] = useToggle();
const [description, setDescription] = useInput(post?.description || "");
const user = useSelector((state) => state.user);
const [files, setFiles] = useState<any[]>(post?.files || []);
const handleFileUpload = (e) => {
const fileList = Array.from(e.target.files);
if (fileList.length > maxFiles || files.length + fileList.length > maxFiles) {
setShowModal(true);
} else {
const clonedFiles = [...files, ...fileList];
setFiles(clonedFiles);
}
e.target.value = "";
};
const removeHandler = (id) => {
const filtered = files.filter((file) => file.name !== id);
setFiles(filtered);
};
return (
<div className={styles.createPost}>
<div className={styles.top}>
<span>
<img src="/static/images/person1.jpg" />
</span>
<textarea
onChange={setDescription}
className="primaryScrollbar"
aria-multiline={true}
value={description}
placeholder={`What's on your mind ${user?.name?.split(" ")[0]}`}
></textarea>
{description || files.length ? (
<StyledButton
background="bgPrimary"
size="md"
className={styles.submitButton}
onClick={() => {
if (!isEdit)
submitHandler({
files: files,
author: { name: user.name, username: user.username },
postedTime: 52345,
id: Math.random() * Math.random() * 123456789101112,
comments: [],
likes: [],
description,
});
else {
submitHandler({
...post,
description,
files,
});
}
setDescription("");
setFiles([]);
}}
>
{isEdit ? "Edit" : "Post"}
</StyledButton>
) : null}
</div>
<div className={styles.middle}>
<div className={styles.row}>
{files.map((file) => {
return (
<Removeable
key={file.name + Math.random() * 100000}
removeHandler={() => {
removeHandler(file.name);
}}
>
{file.type.includes("image") ? (
<img src={URL.createObjectURL(file)} width={150} height={150} />
) : (
<video>
<source src={URL.createObjectURL(file)} type={file.type} />
</video>
)}
</Removeable>
);
})}
</div>
</div>
<div className={styles.bottom}>
<FileUpload
id="uploadPhoto"
label="upload photo"
icon={
<span>
<Camera /> Photo
</span>
}
className={styles.fileUpload}
multiple
onChange={handleFileUpload}
accept="image/*"
/>
<FileUpload
id="uploadVideo"
label="upload video"
icon={
<span>
<Video /> Video
</span>
}
className={styles.fileUpload}
multiple
onChange={handleFileUpload}
accept="video/*"
/>
<FileUpload
id="writeArticle"
label="write article"
icon={
<span>
<Writing /> Article
</span>
}
className={styles.fileUpload}
multiple
onChange={handleFileUpload}
/>
</div>
{showModal && (
<Modal size="sm" backdrop="transparent" ref={ref} closeModal={setShowModal.bind(null, false)} yPosition="top">
<p>Please choose a maximum of {maxFiles} files</p>
<StyledButton size="md" background="bgPrimary" onClick={setShowModal.bind(null, false)}>
Ok
</StyledButton>
</Modal>
)}
</div>
);
};
export default CreatePost;
Now on my main file I have:
const Main = () => {
const [posts, setPosts] = useState<postType[]>([]);
const addPost = (post: postType) => {
setPosts([post, ...posts]);
};
const editPost = (post: postType) => {
const updated = posts.map((p) => {
if (post.id === post.id) {
p = post;
}
return p;
});
setPosts(updated);
};
const deletePost = (id) => {
const filtered = posts.filter((post) => post.id !== id);
setPosts(filtered);
};
return (
<>
<CreatePost submitHandler={addPost} key="0" />
<CreatePost submitHandler={addPost} key="1"/>
{posts.map((post) => {
return <PostItem {...post} editHandler={editPost} key={post.id} deleteHandler={deletePost.bind(null, post.id)} />;
})}
</>
);
};
export default Main;
I tried to add/remove the key but doesn't change anything, also tried to recreate this problem in a simpler way in sandbox but I can't it works fine there. And the problem is only when I upload files not when I write text inside the <textarea/>
Note: The second in reality is shown dynamically inside a modal when clicked edit in a post, but I just showed it here for simplicity because the same problem occurs in both cases.
Okay after some hours of debugging I finally found the problem.
Because my <FileUpload/> uses id to target the input inside the <CreatePost/> the <FileUpload/> always had same it, so when I used <CreatePost/> more than 1 time it would target the first element that found with that id that's why the first component was being updated
I'm trying to display a image selected from my computer in my web app. I referred the following question which addresses the question i'm trying to fix.
How to display selected image without sending data to server?
I have my html part like this
<div className="add_grp_image_div margin_bottom">
<img src={img_upload} className="add_grp_image"/>
<input type="file" className="filetype" id="group_image"/>
<span className="small_font to_middle">Add group image</span>
<img id="target"/>
</div>
I want to display the selected image in
<img id="target"/>
How can i do this?
I referred FileReader docs as well.
https://developer.mozilla.org/en-US/docs/Web/API/FileReader
Try this
<input type="file" onChange={this.onImageChange} className="filetype" id="group_image"/>
Add method to class
onImageChange = (event) => {
if (event.target.files && event.target.files[0]) {
let reader = new FileReader();
reader.onload = (e) => {
this.setState({image: e.target.result});
};
reader.readAsDataURL(event.target.files[0]);
}
}
or you can use URL.createObjectURL
onImageChange = (event) => {
if (event.target.files && event.target.files[0]) {
this.setState({
image: URL.createObjectURL(event.target.files[0])
});
}
}
And display image
<img id="target" src={this.state.image}/>
For the hook version:
const [image, setImage] = useState(null)
const onImageChange = (event) => {
if (event.target.files && event.target.files[0]) {
setImage(URL.createObjectURL(event.target.files[0]));
}
}
return (
<div>
<input type="file" onChange={onImageChange} className="filetype" />
<img src={image} alt="preview image" />
</div>
)
Recently I came across needing a similar feature. Here is my implementation using hooks.
export default function App() {
const [image, setImage] = React.useState("");
const imageRef = React.useRef(null);
function useDisplayImage() {
const [result, setResult] = React.useState("");
function uploader(e) {
const imageFile = e.target.files[0];
const reader = new FileReader();
reader.addEventListener("load", (e) => {
setResult(e.target.result);
});
reader.readAsDataURL(imageFile);
}
return { result, uploader };
}
const { result, uploader } = useDisplayImage();
return (
<div className="App">
<input
type="file"
onChange={(e) => {
setImage(e.target.files[0]);
uploader(e);
}}
/>
{result && <img ref={imageRef} src={result} alt="" />}
</div>
);
}
I created a custom hook so that it can be reused anywhere in the app. The hook returns the image src and the uploader function.
The image src can then be linked to the <img src={..} /> and then on input change you can just pass in the e to the uploader function.
hope it works for you
<form onSubmit={form => submitForm(form)}>
<input
accept="image/*"
onChange={onImageChange}
className={classes.inputImage}
id="contained-button-file"
multiple
name="image"
type="file"
/>
<label htmlFor="contained-button-file">
<IconButton component="span">
<Avatar
src={mydata.imagePreview}
style={{
margin: "10px",
width: "200px",
height: "200px"
}}
/>
</IconButton>
</label>
<Button
type="submit"
variant="outlined"
className={classes.button}
>
Default
</Button>
</form>
onImageChange
const onImageChange = event => {
if (event.target.files && event.target.files[0]) {
let reader = new FileReader();
let file = event.target.files[0];
reader.onloadend = () => {
setData({
...mydata,
imagePreview: reader.result,
file: file
});
};
reader.readAsDataURL(file);
}
};
submitForm
const submitForm = form => {
form.preventDefault();
const formData = new FormData();
formData.append("image", mydata.file);
// for (var pair of formData.entries()) {
// console.log(pair[1]);
// }
const config = {
headers: {
"content-type": "multipart/form-data"
}
};
axios
.post("api/profile/upload", formData, config)
.then(response => {
alert("The file is successfully uploaded");
})
.catch(error => {});
};