How to render an image every 5 seconds in React? - javascript

Every time you enter this url: https://picsum.photos/200, is shown a different image. I want my react component to render every 5 seconds a different image with this url, but I can't do it. This is my code:
import { useEffect, useState } from "react";
const VariableImage = () => {
const imageUrl = "https://picsum.photos/200";
const [image, setImage] = useState(imageUrl);
useEffect(() => {
setInterval(() => {
const newImage = new Image();
newImage.src = imageUrl;
setImage(imageUrl);
}, 5000);
}, [imageUrl]);
return (
<>
<img src={image} alt="scenery" height="200" width="200" />
</>
);
};
export default VariableImage;
An image is shown in first render but later don't change.
If anyone could help me I would be very grateful. Thanks.

Add a dummy randomized query parameter to the external URL so as to force the browser to make a new request (and give you a new image).
Doing new Image isn't helping you any here - you can leave that off entirely.
const { useEffect, useState } = React;
const imageUrl = "https://picsum.photos/200";
const VariableImage = () => {
const [src, setSrc] = useState(imageUrl);
useEffect(() => {
setInterval(() => {
setSrc(imageUrl + '?forcerefresh=' + Math.random());
}, 5000);
}, []);
return <img src={src} alt="scenery" height="200" width="200" />;
};
ReactDOM.createRoot(document.querySelector('.react')).render(<VariableImage />);
<script crossorigin src="https://unpkg.com/react#18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#18/umd/react-dom.development.js"></script>
<div class='react'></div>

'
You are adding the same imageUrl(reference) into it, because of which react
is not able to find any changes so it is not updating the state.
'
Example with vanilla js.
const img = document.querySelector("img");
setInterval(() => {
img.src = "https://picsum.photos/200" + "?forcerefresh=" + Math.random();
}, 5000);
<image src="https://picsum.photos/200" />
Please add clearInterval(id) to stop the time when the component unmounts.
const { useEffect, useState } = React;
const imageUrl = "https://picsum.photos/200";
const VariableImage = () => {
const [src, setSrc] = useState(imageUrl);
useEffect(() => {
const id = setInterval(() => {
setSrc(imageUrl + "?forcerefresh=" + Math.random());
}, 5000);
return () => clearInterval(id)
}, []);
return (
<>
<img src={src} alt="scenery" height="200" width="200" />
</>
)
};

Related

How to use the latest value of the state inside a useEffect?

My component renders an image every 4 seconds. When I click on the image I want to stop rendering new images. For that I've used a useEffect hook. When I click to the image, the state hasToRefresh changes it's values, but inside useEffect it doesn't change. This is my code:
import { useEffect, useState } from "react";
const VariableImage = () => {
const imageUrl = "https://picsum.photos/200";
const imageRefresh = "?forcerefresh=";
const [image, setImage] = useState(imageUrl);
const [hasToRefresh, setHasToRefresh] = useState(true);
useEffect(() => {
if (hasToRefresh) {
setInterval(() => {
setImage(imageUrl + imageRefresh + Math.random());
}, 4000);
}
}, [imageUrl, imageRefresh, hasToRefresh]);
return (
<>
<img
src={image}
onClick={() => setHasToRefresh(!hasToRefresh)}
alt="scenery"
height="200"
width="200"
/>
</>
);
};
export default VariableImage;
Also in sandbox: https://codesandbox.io/s/variable-image-zxhejs
How can I do for when I click the image to not render more images?
If anyone could help me I would be very grateful. Thanks.
You're never stopping your interval. And to only trigger the useEffect() for hasToRefresh, I would move the creation of image string outside of it.
const VariableImage = () => {
const imageUrl = "https://picsum.photos/200";
const imageRefresh = "?forcerefresh=";
const [imageNumber, setImageNumber] = useState(Math.random());
const image = imageUrl + imageRefresh + imageNumber;
const [hasToRefresh, setHasToRefresh] = useState(true);
const intervalRef = useRef();
useEffect(() => {
if (hasToRefresh) {
intervalRef.current = setInterval(() => {
setImageNumber(Math.random());
}, 1000);
}
return () => {
intervalRef.current && clearInterval(intervalRef.current);
intervalRef.current = null;
}
}, [hasToRefresh]);
return (
<>
<img
src={image}
onClick={() => setHasToRefresh(!hasToRefresh)}
alt="scenery"
height="200"
width="200"
/>
</>
);
};
Here's the updated codesandbox: https://codesandbox.io/s/variable-image-forked-oxfgc9?file=/src/VariableImage/VariableImage.js:54-896
As Roy Schut mentioned, you never stop your timer. But the best option would be here to stop the timer when the image shouldn't be refreshed. Here's the code I would prefer.
import { useEffect, useState, useRef } from "react";
const VariableImage = () => {
const imageUrl = "https://picsum.photos/200";
const imageRefresh = "?forcerefresh=";
const [image, setImage] = useState(imageUrl);
const [hasToRefresh, setHasToRefresh] = useState(true);
const intervalRef = useRef(null);
useEffect(() => {
startTimer();
return () => stopTimer();
}, []);
const startTimer = () => {
intervalRef.current = setInterval(() => {
setImage(imageUrl + imageRefresh + Math.random());
}, 4000);
};
const stopTimer = () => {
clearInterval(intervalRef.current);
};
const toggleRefresh = () => {
if (hasToRefresh) {
stopTimer();
} else {
startTimer();
}
setHasToRefresh(state => !state);
};
return (
<>
<img
src={image}
onClick={() => toggleRefresh()}
alt="scenery"
height="200"
width="200"
/>
</>
);
};
export default VariableImage;

How can i give a url to a react components using props?

Component structure's
const IconSkill = (props) => {
return (
<img
onMouseOver={() => {
const skillIcon = document.getElementById("skills-icon");
skillIcon.classList.add("skills-icon-hover");
setTimeout(() => {
skillIcon.classList.remove("skills-icon-hover");
}, 1500);
}}
id="skills-icon"
className="skills-icon"
src={require(props.srcUrl).default}
alt={props.alt}
/>
)
}
Here you see that i am trying to give a relative URL to src img using props, and it isn't working:
<IconSkill srcUrl={"../../images/skills/figma.svg"} alt="HTML Icon" />
Here console log error:
react-refresh-runtime.development.js:315 Uncaught Error: Cannot find
module '../../images/skills/figma.svg'
try to import it by giving some random name if its under "src" folder:
import ImageName form "../../images/skills/figma.svg";
export default function(){
return <IconSkill srcUrl={ImageName} alt="HTML Icon" />
}
If images is under public folder -> images folder then your code should be:
<IconSkill srcUrl={"./images/skills/figma.svg"} alt="HTML Icon" />
and on IconSkill Component:
const IconSkill = (props) => {
return (
<img
onMouseOver={() => {
const skillIcon = document.getElementById("skills-icon");
skillIcon.classList.add("skills-icon-hover");
setTimeout(() => {
skillIcon.classList.remove("skills-icon-hover");
}, 1500);
}}
id="skills-icon"
className="skills-icon"
src={props.srcUrl}
alt={props.alt}
/>
)
}
Your code is generally not a React code. To achieve what you want your code must look smth like this.
import {useState} from "react";
const IconSkill = (props) => {
const {alt, srcUrl} = props;
//you need some state to control it's hover state
const [skillsIcon, setSkillsIcon] = useState(false);
const handleHover = () => {
setSkillsIcon(true);
setTimeout(() => {
setSkillsIcon(false);
}, 1500);
}
return (
<img
onMouseOver={handleHover}
src={srcUrl}
className={skillsIcon ? "skills-icon-hover" : null}
id="skills-icon"
alt={alt}
/>
)
}

react, How do I use face-api as a plain javascript function inside react to detect face from photos?

I'm working on a react project. Where my component will call a JavaScript function(defined in another file) with the image parameter and then my Javascript function which contains face-api.js code will determine the face from image and console the result of face detection box dimensions.
First I tried it inside a react component it worked. But now I'm trying to make it as plain JavaScript module for my other react project but it gives me error
Here is the working the react component for face detection with face-api
FaceRecognition.js
import React, { useState, useEffect, useRef } from "react";
import * as faceapi from "face-api.js";
import Img from "../assets/mFace.jpg";
import './App.css';
const PhotoFaceDetection = () => {
const [initializing, setInitializing] = useState(false);
const [image, setImage] = useState(Img);
const canvasRef = useRef();
const imageRef = useRef();
useEffect(() => {
const loadModels = async () => {
const MODEL_URL = process.env.PUBLIC_URL + "/models";
setInitializing(true);
Promise.all([
faceapi.nets.tinyFaceDetector.loadFromUri(MODEL_URL),
faceapi.nets.faceLandmark68Net.loadFromUri(MODEL_URL),
faceapi.nets.faceRecognitionNet.loadFromUri(MODEL_URL),
faceapi.nets.faceExpressionNet.loadFromUri(MODEL_URL)
])
.then(console.log("success", MODEL_URL)).then(handleImageClick)
.catch((e) => console.error(e));
};
loadModels();
}, []);
const handleImageClick = async () => {
if (initializing) {
setInitializing(false);
}
canvasRef.current.innerHTML = faceapi.createCanvasFromMedia(
imageRef.current
);
const displaySize = {
width: 500,
height: 350
};
faceapi.matchDimensions(canvasRef.current, displaySize);
const detections = await faceapi
.detectSingleFace(imageRef.current, new faceapi.TinyFaceDetectorOptions())
const resizeDetections = faceapi.resizeResults(detections, displaySize);
canvasRef.current
.getContext("2d")
.clearRect(0, 0, displaySize.width, displaySize.height);
faceapi.draw.drawDetections(canvasRef.current, resizeDetections);
console.log(`Width ${detections.box._width} and Height ${detections.box._height}`);
};
return (
<div className="App">
<span>{initializing ? "Initializing" : "Ready"}</span>
<div className="display-flex justify-content-center">
<img ref={imageRef} src={image} alt="face" />
<canvas ref={canvasRef} className="position-absolute" />
</div>
</div>
);
};
export default PhotoFaceDetection;
Not working raising error: Below is my plain JavaScript function which I'm trying to implement from above component.
import * as faceapi from "face-api.js";
const faceDetector = (image) => {
const imageRef = image;
const handleImage = async () => {
const detections = await faceapi
.detectSingleFace(imageRef, new faceapi.TinyFaceDetectorOptions())
console.log(`Width ${detections.box._width} and Height ${detections.box._height}`);
}
const loadModels = async () => {
const MODEL_URL = process.env.PUBLIC_URL + "/models";
Promise.all([
faceapi.nets.tinyFaceDetector.loadFromUri(MODEL_URL),
faceapi.nets.faceLandmark68Net.loadFromUri(MODEL_URL),
faceapi.nets.faceRecognitionNet.loadFromUri(MODEL_URL),
faceapi.nets.faceExpressionNet.loadFromUri(MODEL_URL)
])
.then(handleImage)
.catch((e) => console.error(e));
};
loadModels();
}
export {faceDetector}
Here is the screenshot of the errror I am getting.

How to map out preloaded images from an array in react?

I am trying to preload some images for an image carousel and store them in an array. I seem to have everything working so far except when I try to map the images in the array in to JSX I get an error.
Error: Objects are not valid as a React child (found: [object HTMLImageElement]). If you meant to render a collection of children, use an array instead
Can someone tell me why please?
As a follow up question, my setInterval (which will be used to rotate through the images) isn't starting and I can't work out why so any help with that would be greatly appreciated.
import React, { useEffect, useState } from 'react'
import { CSSTransition } from 'react-transition-group'
import { ImageCarouselContainer, ImageCarouselSlide } from './imagecarousel.styles'
const images = [
'https://images.unsplash.com/photo-1588392382834-a891154bca4d?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2555&q=80',
'https://images.unsplash.com/photo-1441974231531-c6227db76b6e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2551&q=80',
'https://images.unsplash.com/photo-1470813740244-df37b8c1edcb?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2551&q=80'
]
const ImageCarousel = () => {
const [activeImage, setActiveImage] = useState(1);
const [imagesArr, setImagesArr] = useState([])
useEffect(() => {
let loadedImages = []
images.forEach(el => {
let img = new Image()
img.onload = () => {
loadedImages.push(img);
}
img.src = el
})
setImagesArr(loadedImages);
const counter = () => {
if(activeImage < imagesArr.length) {
setActiveImage(activeImage + 1)
} else {
setActiveImage(0)
}
}
const interval = setInterval(counter, 1000)
return () => {
clearInterval(interval);
}
}, [])
return (
<ImageCarouselContainer>
{
imagesArr &&
imagesArr.map((el, idx) => (
<CSSTransition
classNames='image'
timeout={1000}
key={idx}
in={activeImage === idx ? true : false}
unmountOnExit
>
<ImageCarouselSlide
>
{el}
</ImageCarouselSlide>
</CSSTransition>
))
}
</ImageCarouselContainer>
)
}
export default ImageCarousel
you try to put pure html object not react component to render function. so it dosn't has props etc...
change
images.forEach(el => {
let img = new Image()
img.onload = () => {
loadedImages.push(img);
}
img.src = el
}
to
images.forEach(el => {
let img = new Image()
img.onload = () => {
loadedImages.push(<img src={el}>);
}
img.src = el
}

Cannot get the updated the state value useState and UseEffect

I want to get the latest value of the state and use it inline style, but it firstly returns 0 values, then rendering updated the state. However, I cannot assign the values into the style.
const ImageCard = ({ image: { alt_description, urls } }) => {
const [spans, setSpans] = useState(0);
const imageRef = useRef(null);
useEffect(() => imageRef.current.addEventListener('load', () => {
const height = imageRef.current.clientHeight;
const spans = Math.ceil(height / 10);
setSpans({ spans });
}));
return (
<div style={{ gridRowEnd: `span ${spans}` }}>
<img ref={imageRef} alt={alt_description} src={urls.regular} />
</div>
);
}
console output:
10 0
{spans: 15}
{spans: 33}
...
You would not need useEffect for this. You can use the onLoad event of the img tag as follows:
const ImageCard = ({ image: { alt_description, urls } }) => {
const [spans, setSpans] = useState(0);
const imageLoaded = e => {
const height = e.currentTarget.clientHeight;
const spans = Math.ceil(height / 10);
setSpans({ spans });
};
//Dont forget to remove this line
console.log(spans);
return (
<div style={{ gridRowEnd: `span ${spans}` }}>
<img onLoad={imageLoaded} alt={alt_description} src={urls.regular} />
</div>
);
};
Working code sandbox example

Categories