Im trying to change my image every second in react inside of a map. However this doesnt seem to be working. Here is my code:
function ImageGrid() {
const {shell, gridImage} = styles
const squishAnimals = [
'/SquishAnimalDemo(1).png',
'/SquishAnimalDemo(2).png',
'/SquishAnimalDemo(3).png',
'/SquishAnimalDemo(4).png',
'/SquishAnimalDemo(5).png',
'/SquishAnimalDemo(6).png',
'/SquishAnimalDemo(7).png',
'/SquishAnimalDemo(8).png',
'/SquishAnimalDemo(9).png'
];
const [images, setImages] = useState(squishAnimals);
const [currentImage, setCurrentImage] = useState(0);
function changeBackgroundImage() {
let newCurrentImg = 0;
const noOfImages = images.length;
if (currentImage !== noOfImages - 1) {
newCurrentImg += currentImage;
}
setCurrentImage(newCurrentImg);
}
useEffect(() => {
const interval = setInterval(() => changeBackgroundImage(), 1000);
if (interval) {
clearInterval(interval);
}
});
return (
<div className={shell}>
{squishAnimals.map((image, index) => {
return (
index === 4 ?
<img className={gridImage} src={images[currentImage]} alt={`${index}-${image}`}/> :
<img className={gridImage} src={image} alt={`${index}-${image}`}/>
)
})}
</div>
);
}
Any help as to why it may not be working would be great. Thanks :)
I think it's because you immediately cancel the interval in useEffect. I believe you want to return a cleanup function to clear the interval id.
function ImageGrid() {
const {shell, gridImage} = styles
const squishAnimals = [
'/SquishAnimalDemo(1).png',
'/SquishAnimalDemo(2).png',
'/SquishAnimalDemo(3).png',
'/SquishAnimalDemo(4).png',
'/SquishAnimalDemo(5).png',
'/SquishAnimalDemo(6).png',
'/SquishAnimalDemo(7).png',
'/SquishAnimalDemo(8).png',
'/SquishAnimalDemo(9).png'
];
const [images, setImages] = useState(squishAnimals);
const [currentImage, setCurrentImage] = useState(0);
function changeBackgroundImage() {
let newCurrentImg = 0;
const noOfImages = images.length;
if (currentImage !== noOfImages - 1) {
newCurrentImg += currentImage;
}
setCurrentImage(newCurrentImg);
}
useEffect(() => {
const interval = setInterval(() => changeBackgroundImage(), 1000);
return () => {
if (interval) {
clearInterval(interval);
}
}
});
return (
<div className={shell}>
{squishAnimals.map((image, index) => {
return (
index === 4 ?
<img className={gridImage} src={images[currentImage]} alt={`${index}-${image}`} key={images[currentImage]}/> :
<img className={gridImage} src={image} alt={`${index}-${image}`} key = {`${index}-${image}`}/>
)
})}
</div>
);
}
Related
I'm trying to make a ecommerce store and I want to render a <CompanyCard/> component after every 2 <ProductCard/> components.
I've never done anything like this so I'm not sure if I'm doing the right approach
Here is a example of what I want:
<ProductCard/>
<ProductCard/>
<CompanyCard/>
<ProductCard/>
<ProductCard/>
<CompanyCard/>
(and so on..)
but for some reason im getting a blank page with these 2 errors in the console
The above error occurred in the <CompanyCard> component:
Uncaught TypeError: Cannot read properties of undefined (reading '_id')
This is my code (I'm also using React Redux)
function HomePage() {
let counter = 0;
const dispatch = useDispatch();
const productList = useSelector((state) => state.productList);
const { error, loading, products } = productList;
const companyList = useSelector((state) => state.companyList);
const { companies } = companyList;
useEffect(() => {
dispatch(listProducts(), listCompanies());
}, [dispatch]);
return (
<>
<Navbar />
<div className="homePage">
<div className="centerItemsContainer">
<div className="productsContainer">
{products.map((product) => {
counter++;
if (counter % 4 === 0) {
const companyIndex = counter / 4 - 1;
const company = companies[companyIndex];
return (
<>
<CompanyCard company={company} />
<ProductCard product={product} />
</>
);
} else {
return <ProductCard product={product} />;
}
})}
</div>
</div>
</div>
<Footer />
</>
);
}
export default HomePage;
I hope this helps if u want to render company on 4 th place pls change it to 4 same goes for what place u want
const products = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
export const App = () => {
return (
<>
{products.map((product, index) => {
const counter = index + 1;
if (counter % 2 === 0) {
return (
<>
<p>Prod{index}</p>
<p>Company</p>
</>
);
}
return <p>Prod{index}</p>;
})}
</>
);
};
First, it seems like you are simply selecting a company that does not exist. Log companyIndex and see what values you are using.
Also, there is no need to manually keep track of counter, it is the second argument from map, so just write:
{products.map((product, counter) => {
You are getting an error, because your iterator exceeds array length
I would rather use a normal for loop for this:
const products1 = [...Array(10).keys()].map((e) => "p" + e);
const companies1 = [...Array(10).keys()].map((e) => "c" + e);
console.log(getList(products1, companies1));
const products2 = [...Array(10).keys()].map((e) => "p" + e);
const companies2 = [...Array(1).keys()].map((e) => "c" + e);
console.log(getList(products2, companies2));
function getList(products, companies) {
const list = [];
const min = Math.min(products.length, companies.length);
for (
let i = 0, p = 0, c = 0;
i < products.length + companies.length;
i += 1
) {
// every fifth is a company, but only if there is a company left
// or there are no more products
if ((c < companies.length && i % 5 === 4) || p >= products.length) {
list.push(companies[c++]);
} else {
list.push(products[p++]);
}
}
return list;
}
Final component:
function HomePage() {
let counter = 0;
const dispatch = useDispatch();
const productList = useSelector((state) => state.productList);
const { error, loading, products } = productList;
const companyList = useSelector((state) => state.companyList);
const { companies } = companyList;
function getList() {
const list = [];
const min = Math.min(products.length, companies.length);
for (
let i = 0, p = 0, c = 0;
i < products.length + companies.length;
i += 1
) {
if ((c < companies.length && i % 5 === 4) || p >= products.length) {
list.push(<CompanyCard company={companies[c++]} />);
} else {
list.push(<ProductCard product={products[p++]} />);
}
}
return list;
}
useEffect(() => {
dispatch(listProducts(), listCompanies());
}, [dispatch]);
return (
<>
<Navbar />
<div className="homePage">
<div className="centerItemsContainer">
<div className="productsContainer">{getList()}</div>
</div>
</div>
<Footer />
</>
);
}
export default HomePage;
{products.map((item, index) => (
<>
<ProductCard />
{(!!index && !((index + 1) % 5) && <CompanyCard />)}
</>
)}
I am trying to switch between songs in next js using the use effect hook but everything works perfectly when I click the button the song start automatically and the song switch but the color behind the progress bar does match the current time because it getting the progress of the first song that is loaded in the browser how can I fix this using the use Effect or other method?
if you noticed is does not load properly at the first time when you click the button to switch the song but when you click the play / pause button it fixed I don't know why.
Also it give me the last value not the actual value I mean I need for example the 140 but it always gave me the 247 value the prev Value
post: is the part // ! useEffect to change the audio
import React, { useState, useRef, useEffect } from "react";
import styles from "../styles/AudioPlayer.module.css";
import { BiLeftArrowAlt } from "react-icons/bi";
import { BiRightArrowAlt } from "react-icons/bi";
import { FaPlay } from "react-icons/fa";
import { FaPause } from "react-icons/fa";
const AudioPlayer = () => {
// ! state
const [isPlaying, setIsPlaying] = useState(false);
const [duration, setDuration] = useState(0);
const [currentTime, setCurrentTime] = useState(0);
const [audio, setAudio] = useState(null);
const chapters = [
{
// start: 0,
// end: 40,
start: 0,
end: 8,
},
{
start: 135,
end: 140,
// start: 100,
// end: 140,
},
];
const Elvis = "../audios/AudioElvis.mp3";
const GetLucky = "../audios/GetLucky.mp3";
// ! refs
const audioPlayer = useRef(); // ? reference to the audio component
const progressBar = useRef(); // ? reference to the progress bar
const animationRef = useRef(); // ? reference to the animation
// ! useEffect to handle the audio player
useEffect(() => {
const seconds = Math.floor(audioPlayer.current.duration);
setDuration(seconds);
// ! current is a reference to the current item
// ? max is the reference given by the progress bar
progressBar.current.max = seconds;
}, [audioPlayer?.current?.loadedmetadata, audioPlayer?.current?.readyState]);
// ! useEffect to handle the end of the audio
useEffect(() => {
if (currentTime == duration && isPlaying) {
togglePlayPause();
timeTravel(0);
}
}, [currentTime]);
// ! useEffect to change the audio
useEffect(() => {
if (audio && !isPlaying) {
if (audio === Elvis) {
setDuration(140);
} else if (audio === GetLucky) {
setDuration(247);
}
console.log("audio", audio);
console.log("duration", duration);
setDuration(audioPlayer.current.duration);
audioPlayer.current.src = audio;
audioPlayer.current.load(); // ? load the audio
togglePlayPause();
timeTravel(0);
} else if (audio && isPlaying) {
if (audio === Elvis) {
setDuration(140);
} else if (audio === GetLucky) {
setDuration(247);
}
console.log("audio", audio);
console.log(audio === Elvis);
console.log(audio === GetLucky);
console.log("duration", duration);
audioPlayer.current.src = audio;
audioPlayer.current.load(); // ? load the audio
audioPlayer.current.play();
timeTravel(0);
}
}, [audio]);
// ! FUNCTIONS
const calculateTime = (secs) => {
const minutes = Math.floor(secs / 60);
const returnedMinutes = minutes < 10 ? `0${minutes}` : minutes;
const seconds = Math.floor(secs % 60);
const returnedSeconds = seconds < 10 ? `0${seconds}` : seconds;
return `${returnedMinutes}:${returnedSeconds}`;
};
const togglePlayPause = () => {
const prevValue = isPlaying;
setIsPlaying(!prevValue);
if (!prevValue) {
audioPlayer.current.play();
// ? requestAnimationFrame is a function that allows us to run a function
// ? every frame of the animation
animationRef.current = requestAnimationFrame(whilePlaying);
} else {
audioPlayer.current.pause();
cancelAnimationFrame(animationRef.current);
}
};
// ! Creating our animation function => whilePlaying
const whilePlaying = () => {
progressBar.current.value = audioPlayer.current.currentTime;
changePlayerCurrentTime();
animationRef.current = requestAnimationFrame(whilePlaying);
};
// ! Changing the range of the progress bar FUNCTION
const changeRange = () => {
// ! Setting our audio current time to the value of the progress bar
audioPlayer.current.currentTime = progressBar.current.value;
changePlayerCurrentTime();
};
const changePlayerCurrentTime = () => {
// ! Updating the progress bar in CSS
// ? progressBar.current.style.setProperty => takes two arguments
// ? first is the property name
// ? second is the value of the property
// console.log("bar", progressBar.current.value);
// console.log("duration", duration);
progressBar.current.style.setProperty(
"--seek-before-width",
`${(progressBar.current.value / duration) * 100}%`
);
setCurrentTime(progressBar.current.value);
};
const backThirty = () => {
progressBar.current.value = Number(progressBar.current.value) - 30;
changeRange();
};
const forwardThirty = () => {
progressBar.current.value = Number(progressBar.current.value) + 30;
changeRange();
};
const timeTravel = (newTime) => {
progressBar.current.value = newTime;
changeRange();
};
// // ! Changing the audio source
// const changeSong = (song) => {
// setAudio(song);
// setIsPlaying(false);
// timeTravel(0);
// };
return (
<div className={styles.audioPlayer}>
{/* Audio Player Reference */}
<audio
ref={audioPlayer}
// src="https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3"
src={Elvis}
preload="metadata"
></audio>
{/* End of the Audio Player Reference */}
{/* Current Time */}
<div className={styles.currentTime}>{calculateTime(currentTime)}</div>
{/* Progress Bar */}
<div className={styles.progressBarWrapper}>
<input
type="range"
className={styles.progressBar}
defaultValue="0"
ref={progressBar}
onChange={changeRange}
step="0.05"
/>
{/* Chapters Video 2 */}
{chapters.map((chapter, i) => {
const leftStyle = (chapter.start / duration) * 100;
const widthStyle = ((chapter.end - chapter.start) / duration) * 100;
return (
<div
key={i}
className={`${styles.chapter} ${
chapter.start === 0 && styles.start
} ${chapter.end === duration && styles.end}`}
style={{
"--left": `${leftStyle}%`,
"--width": `${widthStyle}%`,
}}
></div>
);
})}
</div>
{/* Duration */}
<div className={styles.duration}>
{duration && !isNaN(duration) ? calculateTime(duration) : "0:00"}
</div>
{/* Controls */}
<div className={styles.controls}>
<button className={styles.forwardBackward} onClick={backThirty}>
<BiLeftArrowAlt /> 30
</button>
<button onClick={togglePlayPause} className={styles.playPause}>
{isPlaying ? <FaPause /> : <FaPlay className={styles.play} />}
</button>
<button className={styles.forwardBackward} onClick={forwardThirty}>
30 <BiRightArrowAlt />
</button>
</div>
<div>
<button onClick={() => setAudio(Elvis)}>Elvis Song</button>
<button onClick={() => setAudio(GetLucky)}>Get Lucky Song</button>
</div>
</div>
);
};
export { AudioPlayer };
// ! the {} means that this is a named export
So my page is an Author page which shows different authors and their details in each card which I fetched from API and then mapped.
https://i.stack.imgur.com/eSD7u.png
And in each card after onclick it changes to Remove Favourite. The card which is favourited makes the idfav true in the object array of the author state and false if not favourited. And there is a 2nd page which shows all the favourite authors. Now I am passing it down first as localstorage for the author state but it seems after my 2nd reload if I click on the button irrespective of whether or not the button is add or remove all the other cards/array is removed and only the card on which button I selected shows up.
const [author, setAuthor] = useState([]);
const [AuthorTempState, setAuthorTempState] = useState([]);
// pagination calculation
const [PageNumber, setPageNumber] = useState(0);
const [Postsperpage] = useState(4);
const PagesVisited = PageNumber * Postsperpage;
const pageCount = Math.ceil(author.length / Postsperpage);
const changePage = ({ selected }) => {
setPageNumber(selected);
}
const getAuthors = async () => {
const res = await fetch(`https://api.quotable.io/authors?limit=30`);
const data = await res.json();
for (const element of data.results) {
element.idfav = false;
}
data.results.sort((a, b) => (a._id > b._id) ? 1 : -1)
setAuthor(data.results);
setAuthorTempState(data.results);
}
const saveAuth = () => {
localStorage.setItem('authors', JSON.stringify(author));
}
const getAuth = () => {
const newAuthors = JSON.parse(localStorage.getItem('authors'));
if (newAuthors && newAuthors.length > 0) {
setAuthor(newAuthors);
} else {
getAuthors();
}
}
useEffect(() => {
// console.log((author));
if (author.length === 0) {
getAuth();
}
saveAuth();
}, [author]);
const favBttn = (Auth) => {
const filterData = AuthorTempState.filter(data => data._id !== Auth._id)
Auth.idfav = true;
const updateAuthor = [Auth, ...filterData]
updateAuthor.sort((a, b) => (a._id > b._id) ? 1 : -1)
setAuthor(updateAuthor)
}
const remfavBttn = (Auth) => {
const filterData = AuthorTempState.filter(data => data._id !== Auth._id)
Auth.idfav = false;
const updateAuthor = [Auth, ...filterData]
updateAuthor.sort((a, b) => (a._id > b._id) ? 1 : -1)
setAuthor(updateAuthor);
}
const Author = author.slice(PagesVisited, PagesVisited + Postsperpage)
return (
<div className="AppWhole">
<AuthorSidebar />
<div className="App">
<div className="author">
{Author.map(
(Author) => (
<div className="authors" key={Author._id}>
{
(Author.idfav) ? (<button className='right' onClick={() => {
remfavBttn(Author);
}}>Remove Favt.</button >) : (<button className='right' onClick={() => {
favBttn(Author);
}}>Add Favt.</button >)
}
<p>Name: {Author.name}</p>
<p>Bio: {Author.bio}</p>
<p>Wiki: <a href='{Author.link}'>{Author.link}</a></p>
</div>
))}
<div id='pageauthor'>
<ReactPaginate
pageCount={pageCount}
onPageChange={changePage}
previousLabel={"<<"}
nextLabel={">>"}
containerClassName={'paginationLinks'}
disabledClassName={'paginationDisabled'}
activeClassName={'paginationActive'}
/>
</div>
</div>
</div>
</div>
);
}
export default Authors;
Please help me I have been stuck on this for a week. Thank you.
Okay, once I read your entire code and then read your issue made it pretty clear what's wrong. The issue is here
const favBttn = (Auth) => {
// notice that you are using AuthorTempState to filter data
// but do you remember initialising it when the data is found in local storage?
// AuthorTempState is currently an empty array.
const filterData = AuthorTempState.filter(data => data._id !== Auth._id)
Auth.idfav = true;
const updateAuthor = [Auth, ...filterData]
updateAuthor.sort((a, b) => (a._id > b._id) ? 1 : -1)
setAuthor(updateAuthor)
}
probably a simple answer to my issue, but it is one that I cannot figure out. My countdown keeps on going past zero and into the negatives, I would like it to end when it hits zero. What am I doing wrong?
Thanks!
const [staticCountdown, setStaticCountdown] = useState(15);
const [countdown, setCountdown] = useState(15);
const setCount = (count) => {
if (!props.timerActive) {
setStaticCountdown(count);
setCountdown(count);
}
};
useEffect(() => {
let interval = null;
if (props.timerActive) {
interval = setInterval(() => {
setCountdown(countdown => countdown - 1);
}, 1000);
} else if (!props.timerActive && countdown === 0) {
clearInterval(interval);
}
return () => clearInterval(interval);
}, [props.timerActive, countdown]);
You should change
else if (!props.timerActive && countdown === 0)
into
(if (!props.timerActive || countdown === 0)
here is an example based on you code:
const {
useEffect,
useState
} = React;
function App(){
const [timerActive, setTimerActive] = useState(true);
const handleTimerActive = () => {
setTimerActive((prev) => !prev);
};
return (
<div className="App">
<Timer timerActive={timerActive} />
<button onClick={handleTimerActive}>
{timerActive ? "Stop" : "Run"}
</button>
</div>
);
}
function Timer(props) {
const [countdown, setCountdown] = useState(15);
useEffect(() => {
let interval = null;
if (props.timerActive) {
interval = setInterval(() => {
setCountdown((countdown) => countdown - 1);
}, 1000);
}
if (!props.timerActive || countdown === 0) {
clearInterval(interval);
}
return () => clearInterval(interval);
}, [props.timerActive, countdown]);
const handleReset = () => {
setCountdown(15);
};
return (
<div>
<h1>{countdown}</h1>
<button onClick={handleReset}>Reset</button>
</div>
);
}
ReactDOM.render( <App/> ,
document.getElementById("root")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
I am trying to manipulate the images shown in my React App by voice. I implemented the SR, which works fine:
<button onClick={SpeechRecognition.startListening}>Start</button>
I have an array of loadImages in my state, which is empty first:
const [loadImages, setLoadImages] = React.useState([]);
Whenever the word spoken is "kitten", the array of loadImages gets updated in this way:
if(transcript == "kitten")
{
const newImages = loadImages.concat({image: 'https://www.onlinekittencare.com/wp-content/uploads/2020/07/vChK6pTy3vN3KbYZ7UU7k3-1200-80.jpg'})
setLoadImages(newImages);
}
The transcript you see is a variable, which I initialized here:
const {transcript} = useSpeechRecognition();
In the render I use it to show what the SR understood, so if I say "hello" it shows "hello":
<p id="transcript">Transcript: {transcript}</p>
And this is where the images in loadImages show up:
{images.map((image) => {
return <URLImage image={image}/>;
})}
The problem is that whenever I say "kitten", which as stated above is used as a command to add the picture to the array loadImages, my React App gets blank. In the inspect I can also see that it says react-dom.development.js:14997 Uncaught Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.
How do I fix that?
EDIT (I added the whole code):
function App() {
const [rectangles, setRectangles] = useState([]);
const [circles, setCircles] = useState([]);
const [selectedId, selectShape] = useState(null);
const [shapes, setShapes] = useState([]);
const [, updateState] = React.useState();
const stageEl = React.createRef();
const layerEl = React.createRef();
const fileUploadEl = React.createRef();
const [brushSize, setBrushSize] = React.useState('5');
const [isDrawing, setIsDrawing] = React.useState(false);
const dragUrl = React.useRef();
//const stageRef = React.useRef();
const [images, setImages] = React.useState([]);
const [loadImages, setLoadImages] = React.useState([]);
const getRandomInt = max => {
return Math.floor(Math.random() * Math.floor(max));
};
const {transcript} = useSpeechRecognition();
const URLImage = ({image}) => {
const [img] = useImage(image.src);
return (
<Image
image = {img}
x = {image.x}
y = {image.y}
offsetX = {50}
offsetY = {50}
width={200}
height={200}
draggable
/>
);
};
const drawLine = () => {
setIsDrawing(true);
if(isDrawing){
addLine(stageEl.current.getStage(), layerEl.current, brushSize);
};
};
const eraseLine = () => {
addLine(stageEl.current.getStage(), layerEl.current, brushSize, "erase");
};
const addRectangle = () => {
setIsDrawing(false);
const rect = {
x: getRandomInt(100),
y: getRandomInt(100),
width: 100,
height: 100,
fill: "red",
id: `rect${rectangles.length + 1}`,
};
const rects = rectangles.concat([rect]);
setRectangles(rects);
const shs = shapes.concat([`rect${rectangles.length + 1}`]);
setShapes(shs);
};
const forceUpdate = React.useCallback(() => updateState({}), []);
const undo = () => {
const lastId = shapes[shapes.length - 1];
let index = circles.findIndex(c => c.id == lastId);
if (index != -1) {
circles.splice(index, 1);
setCircles(circles);
}
index = rectangles.findIndex(r => r.id == lastId);
if (index != -1) {
rectangles.splice(index, 1);
setRectangles(rectangles);
}
index = images.findIndex(r => r.id == lastId);
if (index != -1) {
images.splice(index, 1);
setImages(images);
}
shapes.pop();
setShapes(shapes);
forceUpdate();
};
document.addEventListener("keydown", ev => {
if (ev.code == "Delete") {
let index = circles.findIndex(c => c.id == selectedId);
if (index != -1) {
circles.splice(index, 1);
setCircles(circles);
}
index = rectangles.findIndex(r => r.id == selectedId);
if (index != -1) {
rectangles.splice(index, 1);
setRectangles(rectangles);
}
index = images.findIndex(r => r.id == selectedId);
if (index != -1) {
images.splice(index, 1);
setImages(images);
}
forceUpdate();
}
});
if(transcript == "kitten")
{
const newImages = loadImages.concat({image: 'https://www.onlinekittencare.com/wp-content/uploads/2020/07/vChK6pTy3vN3KbYZ7UU7k3-1200-80.jpg'})
setLoadImages(newImages);
}
return (
<div className="home-page">
{loadImages.map(image => (
<img id="img" className="img"
src={image.image}
width="200"
height="200"
onDragStart={(e) => {
dragUrl.current = e.target.src;}}
/>
))}
<div
onDrop={(e) => {
e.preventDefault();
// register event position
stageEl.current.setPointersPositions(e);
// add image
setImages(
images.concat([
{
...stageEl.current.getPointerPosition(),
src: dragUrl.current,
},
])
);
}}
onDragOver={(e) =>
e.preventDefault()
}
>
<h1>Whiteboard</h1>
<button onClick={addRectangle}>
Rectangle
</button>
<button>
Circle
</button>
<button onClick={drawLine}>
Line
</button>
<button onClick={eraseLine}>
Erase
</button>
<select
value={brushSize}
onChange={(e) => {
setBrushSize(e.target.value);
drawLine();
}}
>
<option value="5">5</option>
<option value="20">20</option>
<option value="50">50</option>
</select>
<button variant="secondary">
Text
</button>
<button variant="secondary">
Image
</button>
<button variant="secondary" onClick={undo}>
Undo
</button>
<p id="transcript">Transcript: {transcript}</p>
<button onClick={SpeechRecognition.startListening}>Start</button>
<Stage
width={window.innerWidth * 0.9}
height={window.innerHeight - 150}
ref={stageEl}
dragabble
onMouseDown={e => {
// deselect when clicked on empty area
const clickedOnEmpty = e.target === e.target.getStage();
if (clickedOnEmpty) {
selectShape(null);
}
}}
>
<Layer ref={layerEl}>
{rectangles.map((rect, i) => {
return (
<Rectangle
key={i}
shapeProps={rect}
isSelected={rect.id === selectedId}
//onSelect={() => {
// selectShape(rect.id);
//}}
onChange={newAttrs => {
const rects = rectangles.slice();
rects[i] = newAttrs;
setRectangles(rects);
}}
/>
);
})}
{images.map((image) => {
return <URLImage image={image}/>;
})}
</Layer>
</Stage>
</div>
</div>
);
}
export default App;
Based on the code you've shared, it has to do with how you're updating the state if the transcript is equal to kitten.
Essentially, the logic you've written says, on render, if the transcript is kitten, update the state. BUT, when you update the state, that will re-render, and hit that logic again... and again... and again. The solution here is to wrap that in a useEffect – React Docs explain it best but in simple terms, you want to "do something" as a side effect of "something else".
In this case, if the transcript updates, you want to check the state of transcript, and if it meets a condition, you want to update your state:
React.useEffect(() => {
if (transcript === "kitten") {
const newImages = loadImages.concat({image: 'https://www.onlinekittencare.com/wp-content/uploads/2020/07/vChK6pTy3vN3KbYZ7UU7k3-1200-80.jpg'})
setLoadImages(newImages);
}
}, [transcript]);
The final piece to the useEffect is a dependency array ([transcript])–this dictates to React which item you want to watch for changes in–if transcript changes, it will run your effect and only when it changes, instead of every time it renders.