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

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;

Related

I can't preview the image using FileReader

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.

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.

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";

dispatch inside useEffect opposite result

I am displaying a foto in the front using Leigh Halliday's Image Previews in React with FileReader from https://www.youtube.com/watch?v=BPUgM1Ig4Po and everything is super BUT:
1.I want to get information from the image is displaying, exactly the base64 info, and have it then globally in my reactjs app.
2.for that reason I made a Context, i configured it ok BUT:
when I am doing dispatch inside a useEffect I want the image rendering and the info store in my variable globally
but I have one thing or another
if my image renders ok in my front, I can not obtain the value of my dispatch and viceversa
this is the code of my component:
import React, { useContext, useEffect, useRef, useState } from 'react'
import { AuthContext } from '../../auth/AuthContext'
import { types } from '../../types/types'
export const ButtonLoadFoto = () => {
const { dispatchFoto } = useContext(AuthContext)
const [image, setImage] = useState('')
const [preview, setPreview] = useState('')
const [status, setStatus] = useState(false)
useEffect(() => {
if (image) {
const reader = new FileReader()
reader.onloadend = () => {
setPreview(reader.result)
}
reader.readAsDataURL(image)
setStatus(true)
} else {
setPreview('')
}
}, [image])
// useEffect(() => {
// if (status) {
// dispatchFoto({
// type: types.foto,
// payload: {
// foto: preview.split(',')[1]
// }
// })
// }
// return () => setStatus(false)
// }, [preview])
const fileInputRef = useRef()
const handleRef = (e) => {
e.preventDefault()
fileInputRef.current.click()
}
const handleFile = (e) => {
const file = e.target.files[0]
if (file && file.type.substr(0, 5) === 'image') {
setImage(file)
}
}
return (
<div className='load-input '>
{
preview
?
(<img src={preview} alt='' onClick={() => setImage('')} />)
:
(<button
className='alert alert-danger'
onClick={handleRef}>
foto
</button>
)
}
< input
type='file'
style={{ display: 'none' }}
ref={fileInputRef}
accept='image/*'
onChange={handleFile}
/>
</div>
)
}
in the code above if you put away the comments we will have the information we want but the foto won t display at all
thanks all for your time , I really appreciate!
EDIT
this is the main component
import React, { useEffect, useReducer } from 'react'
import { AuthContext } from './auth/AuthContext'
import { fotoReducer } from './components/formScreen/fotoReducer'
import { AppRouter } from './routers/AppRouter'
const initImage = () => {
return { foto: '' }
}
export const CMI = () => {
const [foto, dispatchFoto] = useReducer(fotoReducer, {}, initImage)
return (
<div>
<AuthContext.Provider value={{
foto,
dispatchFoto
}}>
<AppRouter />
</AuthContext.Provider>
</div>
)
}
this is the componenent I use
import React, { useContext} from 'react'
import { ButtonLoadFoto } from '../components/formScreen/ButtonLoadFoto'
import { AuthContext } from '../auth/AuthContext'
export const FormScreen = () => {
const { foto } = useContext(AuthContext)
}
return (
<div>
<ButtonLoadFoto/>
</div>
)
as I said : if a render the image I can not have the information and viceversa...
when I use dispatch I don t know I it brokes the image render
thanks in advance

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