Reputation: 77
I have a component that displays a grid with images. Upon clicking a specific image, a modal pops up with buttons for navigation on either side of the image. To display the images, i map through the docs array and access the doc.url. There is also an onClick which passes the doc.url and index of the item which then updates the state of selectedImg. Here is the ImageGrid.js component code:
const ImageGrid = ({ setSelectedImg, imageIndex, setImageIndex }) => {
const { docs } = useFirestore('images');
console.log('current docs', docs)
const clickHandler = (url, index) => {
setSelectedImg(url);
setImageIndex(index);
}
return (
<div className='img-grid'>
{ docs && docs.map((doc, index) => (
<motion.div className='img-wrap' key={doc.id}
layout
whileHover={{ opacity: 1 }}
whileTap={{ scale: 0.9 }}
onClick={() => clickHandler(doc.url, index)}
>
<motion.img src={doc.url} alt='uploaded pic'
initial={{ opacity: 0 }}
animate={{ opacity: 1}}
transition={{ delay: 1 }}
/>
</motion.div>
)) }
</div>
)
}
I then have a Modal.js component with two onClick functions for previous and next buttons next to the modal image. See as follows:
const Modal = ({ selectedImg, setSelectedImg, imageIndex, setImageIndex }) => {
const { docs } = useFirestore('images');
const handleClick = (e) => {
if(e.target.classList.contains('backdrop'))
setSelectedImg(null);
}
// Navigation
const prevImage = () => {
setImageIndex(prev => prev - 1);
setSelectedImg(docs[imageIndex].url);
}
const nextImage = () => {
setImageIndex(prev => prev + 1);
setSelectedImg(docs[imageIndex].url);
}
return (
<motion.div className='backdrop' onClick={handleClick}
initial={{ opacity: 0}}
animate={{ opacity: 1}}
>
<FaChevronCircleLeft className='modal-navigation left' onClick={prevImage} />
<motion.img src={selectedImg} alt='enlarged pic'
initial={{ y: '-100vh'}}
animate={{ y: 0 }}
/>
<FaChevronCircleRight className='modal-navigation right' onClick={nextImage}/>
</motion.div>
)
}
This does work but the problem is that once you select an image from the grid and the modal appears for the first time, if you click either navigation button, nothing happens. Then on the second click, it starts to work. From my understanding, this is because the imageIndex state is updating after the button has been clicked, thefore useEffect needs to be used.
I tried to use useEffect in Modal.js where it would only work if [imageIndex] was a dependency. I kept receiving an error because setImageIndex was being used even before the Modal navigation was used.
// Navigation
const prevImage = () => {
setImageIndex(prev => prev - 1);
}
const nextImage = () => {
setImageIndex(prev => prev + 1);
}
useEffect(() => {
setSelectedImg(docs[imageIndex].url);
}, [imageIndex])
How can I pass the index of the current image and have it correspond with the navigation buttons when they're clicked?
Thanks in advance.
Upvotes: 0
Views: 62
Reputation: 77
I worked it out. In the Modal.js, I added another piece of state for when the navigation button is clicked. It starts off as false and when the left or right arrow is clicked, the onClick function sets the buttonClicked state to true; The onClick also sets the imageIndex - 1 or + 1 using the previous props.
Then, i added a useEffect function that basically said, every time the button was clicked, setSelectedImg to the image with the new imageIndex. And then set the buttonClicked back to false. [buttonClicked] is the dependency. See code:
const [ buttonClicked, setButtonClicked ] = useState(false);
const handleClick = (e) => {
if(e.target.classList.contains('backdrop'))
setSelectedImg(null);
}
// Navigation
const prevImage = () => {
setImageIndex(prev => prev - 1);
setButtonClicked(true);
}
const nextImage = () => {
setImageIndex(prev => prev + 1);
setButtonClicked(true);
}
useEffect(() => {
if (buttonClicked){
setSelectedImg(docs[imageIndex].url)
setButtonClicked(false);
};
}, [buttonClicked])
With the click event handlers, no need to use if statements for imageIndex arriving at a negative number or more than the amount of images in the array. I just used inline if statements to show or hide the buttons:
{ imageIndex !== 0 && <FaChevronCircleLeft className='modal-navigation left' onClick={prevImage} /> }
{ imageIndex !== docs.length - 1 && <FaChevronCircleRight className='modal-navigation right' onClick={nextImage}/> }
Upvotes: 0