I don't know if I need to use global state like useContext for this ( I am not using redux in this project) but what I want to do is, once I have uploaded a new image it sends the image data back from the server and I set that state. I want to then replace the existing image on the screen with the newly uploaded one.
So, here is my file input component:
import React, { useState } from "react";
import ProgressBar from "../../../shared/components/progressBar/ProgressBar";
const UploadForm = () => {
const [file, setFile] = useState(null);
const [error, setError] = useState(null);
const types = ["image/png", "image/jpg", "image/jpeg"];
const changeHandler = (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>
<input type="file" onChange={changeHandler} name="image"></input>
<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;
My progress bar component:
import React, { useEffect } from "react";
import useStorage from "../../hooks/use-storage";
import { motion } from "framer-motion";
import "./ProgressBar.css";
const ProgressBar = ({ file, setFile }) => {
const { url, progress } = useStorage(file);
useEffect(() => {
if (url) {
setFile(null);
}
}, [url, setFile]);
return (
<motion.div
className="upload-progress"
initial={{ width: 0 }}
animate={{ width: progress + "%" }}
></motion.div>
);
};
export default ProgressBar;
And, my upload custom hook. You can see here I am setting the state of the image url here but I don't know how to then update my Avatar component once the upload is complete.
import React, { useState, useEffect, useContext } from "react";
import Axios from "axios";
import { AuthContext } from "../context/auth-context";
const useStorage = (file) => {
const auth = useContext(AuthContext);
const [progress, setProgress] = useState(0);
const [error, setError] = useState(null);
const [url, setUrl] = useState(null);
useEffect(() => {
const formData = new FormData();
formData.append("image", file);
try {
const sendImage = async () => {
const response = await Axios.post(
"http://localhost:8000/api/v1/users/update-avatar",
formData,
{
headers: {
"Content-type": "multipart/form-data",
Authorization: "Bearer " + auth.token,
},
onUploadProgress: (progressEvent) => {
setProgress(
parseInt(
Math.round((progressEvent.loaded * 100) / progressEvent.total)
)
);
},
}
);
// get the new file name from the server so you can show it right away after upload
const { filename, path } = response.data.file;
setUrl({ filename, path });
};
sendImage();
} catch (err) {
setError(err);
console.log(err.response);
}
}, [file]);
return { progress, url, error };
};
export default useStorage;
Avatar component
import React, { useContext, useEffect, useState } from "react";
import Axios from "axios";
import { AuthContext } from "../../../shared/context/auth-context";
const Avatar = () => {
const auth = useContext(AuthContext);
const [avatar, setAvatar] = useState(null);
useEffect(() => {
const getAvatarImage = async () => {
const response = await Axios.get(
"http://localhost:8000/api/v1/users/get-avatar",
{ headers: { Authorization: "Bearer " + auth.token } }
);
setAvatar(response.data.avatar);
};
if (auth.token) getAvatarImage();
}, [auth.token]);
return (
<div>
{avatar && (
<img
src={`http://localhost:8000/uploads/images/${avatar}`}
width="200"
alt="avatar image"
/>
)}
</div>
);
};
export default Avatar;
Auth Context
import { createContext } from "react";
export const AuthContext = createContext({
isLoggedIn: false,
userId: null,
token: null,
email: null,
firstName: null,
login: () => {},
logout: () => {},
});