I can't preview the image using FileReader - reactjs

I have a problem with filereader in reactjs, indeed I would like to preview the image after loading but the state manager does not change and the image doesn't load
import React, { useState } from "react";
import { useSelector, useDispatch } from "react-redux";
import { isEmpty } from "../../Components/Utils";
import Img from "../../styles/assets/icons/img.svg";
import { createProduct } from "../../actions/product.action";
const AddProduct = () => {
const [productText, setProductText] = useState("");
const [productFile, setProductFile] = useState();
const [productPrice, setProductPrice] = useState(0);
const [productImg, setProductImg] = useState("");
const [isDownload, setIsDownload] = useState(false);
const [preview, setPreview] = useState("");
const categories = useSelector((state) => state.categoriesReducer);
const dispatch = useDispatch();
const handlePreviewImg = (e) => {
const reader = new FileReader();
reader.onLoad = () => {
if (reader.readyState === 2) {
setPreview(reader.result);
setIsDownload(true);
}
};
reader.readAsDataURL(e.target.files[0]);
setProductFile(e.target.files[0]);
};
then I try to record in the input file tag so that the upload can be taken into account
<div className="dashboard__categories__form__picture add__product__picture">
<input
type="file"
name="product"
id="file"
accept=".jpg, .jpeg, .png"
className="inputfile"
onChange={handlePreviewImg}
/>
<label htmlFor="file">
{!isDownload ? (
<img src={Img} alt="icons" />
) : (
<img src={preview} alt="categorie-pic" />
)}
</label>
</div>
What is the problem? please help

I believe you don't need to use FileReader.
Maybe you can use URL.createObjectURL
const handlePreviewImg = (e) => {
const blobUrl = URL.createObjectURL(e.target.files[0]);
setPreview(blobUrl);
}
createObjectURL may cause memory leak so you should read the document.

Related

'e.target.files' is possibly 'null'

In my input component I extract file content after input. Took code from here
Error occurred on the line of onChange={e => handleFileChosen(e.target.files[0])}, to be more accurate, ts underlines e.target.files.
Error message is 'e.target.files' is possibly 'null'
And here is the code:
import styles from './Input.module.scss'
import { FileInputProps } from './FileInput.props';
import cn from 'classnames'
import { useState } from 'react';
export const FileInput = ({ ...props }: FileInputProps): JSX.Element => {
const [fileContent, setFileContent] = useState<string>('')
const handleFileChosen = (file: File) => {
let fileReader = new FileReader()
fileReader.onloadend = (e: Event) => {
const content = fileReader.result || ''
setFileContent(content.toString())
}
fileReader.readAsText(file)
}
return (
<>
<input
type='file'
onChange={(e) => {handleFileChosen(e.target.files[0])}}
/>
<p>{fileContent}</p>
</>
)
}
I've checked this SO page, but none of those solutions helped me
Please review this code
import React, {ChangeEvent, useEffect, useState} from 'react';
const FileUploadComponent = () => {
const [base64Data, setBase64Data] = useState("");
const [error, setError] = useState("");
const handleFileChange = (e: ChangeEvent<HTMLInputElement>) => {
const files: FileList | null = e.target.files;
const reader: FileReader = new FileReader();
reader.onloadend = function () {
setBase64Data(reader.result as string)
}
reader.onerror = function () {
setError("Error file not reading");
}
reader.readAsDataURL(files?.[0] as File);
}
useEffect(()=> {
console.log(base64Data)
}, [base64Data])
return (
<div>
<input
className={"form-control"}
type={"file"}
onChange={handleFileChange}
/>
</div>
);
};
export default FileUploadComponent;

Firebase image uploads two times

I am trying to create gallary app and when i try to upload image in fire storage there is only one image but in fire store there are two entries for same image so i think it is happening because it uploads image two times i try to figure out but nothing is working
Here is my use storage hook
import React, { useState, useEffect } from 'react'
import { fireStorage, fireStore } from '../firebase-config';
import { collection, addDoc, serverTimestamp } from "firebase/firestore";
import { ref, getDownloadURL, uploadBytesResumable } from "firebase/storage";
export default function useStorage(file) {
const [progresspercent, setProgresspercent] = useState(0);
const [error, setError] = useState(null);
const [url, setImgUrl] = useState(null);
useEffect(() => {
const storageRef = ref(fireStorage, `files/${file.name}`);
const uploadTask = uploadBytesResumable(storageRef, file);
uploadTask.on("state_changed",
(snapshot) => {
const progress =
Math.round((snapshot.bytesTransferred / snapshot.totalBytes) * 100);
setProgresspercent(progress);
},
(error) => {
setError(error);
},
() => {
getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => {
setImgUrl(downloadURL)
addDoc(collection(fireStore, 'images'),{
url: downloadURL,
createdAt: serverTimestamp()
})
});
}
);
},[file]);
return { progresspercent, url, error};
}
Here is my upload form
import { useState } from "react";
import ProgressBar from './ProgressBar'
function UploadForm() {
const [file, setFile] = useState(null);
const [error, setError] = useState("");
const allowedType = ["image/png", "image/jpg", "image/jpeg"];
const changeHandler = (e) => {
e.preventDefault()
let selectedFile = e.target.files[0]
if (selectedFile && allowedType.includes(selectedFile.type)){
setFile(selectedFile);
setError('')
}else{
setFile(null);
setError("Please select an image file");
}
};
return (
<form>
<label>
<input type="file" onChange={changeHandler} />
<span>+</span>
</label>
<div className="output">
{error && <div className="error">{error}</div>}
{file && <div>{file.name}</div>}
{file && <ProgressBar file={file} setFile={setFile} />}
</div>
</form>
);
}
export default UploadForm;
and Here is my progress bar
import React, { useEffect } from "react";
import useStorage from "..//../src/Hooks/useStorage";
export default function ProgressBar({ file, setFile }) {
const { url, progresspercent } = useStorage(file);
useEffect(() => {
if (url) {
setFile(null);
}
}, [url, setFile]);
return (
<div
className="progress-bar"
style={{ width: progresspercent + "%" }}
></div>
);
}
I don't have any other component, i try to fix it but i don't know why it is happening at first place.

How to set user input to be capitalised

I am trying to access the https://docs.openaq.org/ api and I have created a search function, where you can type the name of the city which should set a state which then is added to the url used to the data. However the user input must be capitalised and I can't seem to get it to work.
import React, {useState} from 'react';
import Axios from 'axios';
import Button from '../UI/Button';
import SearchedCity from './SearchedCity';
const Search = () => {
const [searchedCity, setSearchedCity] = useState('');
const [city, setCity] = useState();
const [airQuality, setAirQuality] = useState();
const [dateAndTime, setDateAndTime] = useState();
const [latitude, setLatitude] = useState();
const [longitude, setLongitude] = useState();
const [country, setCountry] = useState();
const searchCity = async () => {
try{
const url = `https://api.openaq.org/v1/measurements?country=GB&city=${searchedCity}`;
const res = await Axios.get(url);
console.log(res)
} catch (error) {
alert('Please learn to spell');
}
}
const handleSubmit = (e) => {
e.preventDefault();
searchCity();
console.log({searchedCity})
}
const handleChange = (e) => {
let userInput = e.target.value;
userInput.charAt(0).toUpperCase()
console.log(userInput)
setSearchedCity(userInput)
}
return (
<div>
<form onSubmit={handleSubmit} className="form" >
<label>
<input type="text" placeholder="Search for a UK city" onChange={handleChange} />
<Button handleSubmit={handleSubmit}></Button>
</label>
</form>
<SearchedCity city={city} ></SearchedCity>
</div>
);
};
export default Search;
Try something like this
userInput = userInput.charAt(0).toUpperCase() + userInput.slice(1);
It will capitalise the first letter but keep the rest of the string same.
I think the issue is that you don't set the value after changing that character.
So, just do that.
const handleChange = (e) => {
let userInput = e.target.value;
userInput = userInput.charAt(0).toUpperCase()
console.log(userInput)
setSearchedCity(userInput)
}

Why can't i import my component to another component in React JS?

I'm trying to import my ProgressBar component to my UploadForm component but it isn't working. The console announce mistake as: "./src/components/UploadForm.js
Module not found: Can't resolve './components/ProgressBar' in 'C:\Users\Admin\react-website\src\components'". I don't know what wrong? Can anybody help me? Thank you so much!
This is my ProgressBar.js:
import React from "react";
import useStorage from "../hooks/useStorage";
const ProgressBar = () => {
return ( <
div className = "progress-bar" > progress < /div>
)
};
export default ProgressBar;
This is my UploadForm.js:
import React, { useState } from "react";
import ProgressBar from "./components/ProgressBar";
const UploadForm = () => {
const [file, setFile] = useState(null); //to begin with we dont select a file
const types = ["image/png", "image/jpeg"];
const [error, setError] = useState(null);
const changeHandler = (e) => {
let selected = e.target.files[0];
if (selected && types.includes(selected.type)) {
setFile(selected);
setError("");
} else {
setFile(null);
setError("pls select an image file (png or jpeg)");
}
};
return (
<form>
<input type="file" onChange={changeHandler} />{" "}
<div className="output">
{" "}
{error && <div className="error"> {error} </div>}{" "}
{file && <div> {file.name} </div>}{" "}
{file && <ProgressBar file={file} setFile={setFile} />}{" "}
</div>{" "}
</form>
);
};
export default UploadForm;
This is my useStorage.js:
import {
useState,
useEffect
} from 'react'
import {
projectStorage
} from "../firebase/config"
const useStorage = (file) {
const [progress, setProgress] = useState(0)
const [error, setError] = useState(null)
const [url, setURL] = useState(null)
//going to fire everytime the dependency(file) changes
useEffect(() => {
//references
const storageRef = projectStorage.ref(file.name)
storageRef.put(file).on('state_changed', (snap) {
let percentage = (snap.bytesTransferred / snap.totalBytes) * 100
setProgress(percentage)
}, (err) => {
setError(error)
}, async() => {
const url = await storageRef.get.DownloadURL()
setUrl(url)
})
}, [file]);
return {
progress,
url,
error
}
}
export default useStorage;
Directory:
import ProgressBar from "./components/ProgressBar";
The . means the current directory, and UploadForm.js is in the src/components directory. So this line is trying to access src/components/components/ProgressBar, which doesn't exist. Change it to:
import ProgressBar from './ProgressBar";

How to use custom hook>

I am uploading an image and title for that image to firebase. I have made a custom hook(useStorage) which is used for uploading image and title for that image. I have two separate components UploadForm and ProgressBar, I am passing title and selected image and title from UploadForm to ProgressBar. I am able to make fields for image and title in firestore, image is getting uploaded but title field is remaining empty string.
I am attaching code for 3 files, any help would be appreciated.
import { useState, useEffect } from 'react';
import { projectStorage, projectFirestore, timestamp } from '../firebase/config';
const useStorage = (file) => {
const [progress, setProgress] = useState(0);
const [error, setError] = useState(null);
const [url, setUrl] = useState(null);
const [title, setTitle] = useState('');
useEffect(() => {
// references
const storageRef = projectStorage.ref(file.name);
const collectionRef = projectFirestore.collection('images');
storageRef.put(file).on('state_changed', (snap) => {
let percentage = (snap.bytesTransferred / snap.totalBytes) * 100;
setProgress(percentage);
}, (err) => {
setError(err);
}, async () => {
const url = await storageRef.getDownloadURL();
const createdAt = timestamp();
await collectionRef.add({ url, createdAt, title });
setUrl(url);
setTitle(title);
});
}, [file, title]);
return { progress, url, error, title };
}
export default useStorage;
import React, { useState } from 'react';
import ProgressBar from './ProgressBar';
const UploadForm = () => {
const [file, setFile] = useState(null);
const [error, setError] = useState(null);
const [header, setHeader] = useState('')
const types = ['image/png', 'image/jpeg'];
const handleChange = (e) => {
let selected = e.target.files[0];
if (selected && types.includes(selected.type)) {
setFile(selected);
setError('');
} else {
setFile(null);
setError('Please select an image file (png or jpg)');
}
};
return (
<form>
<label>
<input type="file" onChange={handleChange} />
<span>+</span>
</label>
<input placeholder="Enter title for pic..." type="text" name={header} onChange={(e)=> setHeader(e.target.value)} />
<div className="output">
{ error && <div className="error">{ error }</div>}
{ file && <div>{ file.name }</div> }
{ file && <ProgressBar file={file} setFile={setFile} header={header} /> }
</div>
</form>
);
}
export default UploadForm;
import React, { useEffect } from 'react';
import useStorage from '../hooks/useStorage';
import { motion } from 'framer-motion';
const ProgressBar = ({ file, setFile, header }) => {
const { progress, url, title } = useStorage(file);
useEffect(() => {
if (url) {
setFile(null);
}
}, [url, setFile]);
return (
<motion.div className="progress-bar"
initial={{ width: 0 }}
animate={{ width: progress + '%' }}
></motion.div>
);
}
export default ProgressBar;
useStorage have a [title, setTitle] hook which you use in
await collectionRef.add({ url, createdAt, title });
setUrl(url);
setTitle(title);
But the problem is that the title is empty. You never set a title. You pass the header to the progress bar, but you don't use it. You should update useStorage two take to arguments: file and header and call useStorage(file, header)
Update useStorage signature to const useStorage = (file, header) => {}
Inside useStorage you can update const [title, setTitle] = useState(header)
As far as I can tell you never get title from anywhere. All you do is send your state variable title which is initialized with '' to Firestore, then setTitle which that same empty string title.
Did you mean to pass header into your hook alongside file?

Resources