Modal with image slideshow with react - javascript

Hi I am trying to implement a modal with slideshow for next images. I am using unsplash api to fetch images.
The problem I am facing is whenever I am trying to click a random image in a grid, it shows only the first image in Modal.
This is my implementation for setting up the images and modal functionality. I am using the react-modal package for setting up my Modal.
imageList = Array of objects containing image data
url = An object containing image url.
export default function Images({ imageList, url, id }) {
const [modalIsOpen, setModalIsOpen] = useState(false);
const [current, setCurrent] = useState(0);
const length = imageList.length;
function openModal() {
setModalIsOpen(true);
}
function closeModal() {
setModalIsOpen(false);
}
const nextSlide = () => {
setCurrent(current === length - 1 ? 0 : current + 1);
};
const prevSlide = () => {
setCurrent(current === 0 ? length - 1 : current - 1);
};
if (!Array.isArray(imageList) || imageList.length <= 0) {
return null;
}
return (
<div>
<img onClick={openModal} key={id} src={url.urls.thumb} alt='' />
<Modal
ariaHideApp={false}
closeTimeoutMS={200}
style={customStyles}
isOpen={modalIsOpen}
onRequestClose={closeModal}
contentLabel='Image modal'
>
<AiOutlineArrowLeft
className='left-arrow'
onClick={prevSlide}
/>
<AiOutlineArrowRight
className='right-arrow'
onClick={nextSlide}
/>
{imageList.map((image, id) => {
return (
<div
className={
id === current ? 'slide active' : 'slide'
}
key={id}
>
{id === current && (
<img
alt=''
className='image'
src={image.urls.small}
/>
)}
</div>
);
})}
</Modal>
</div>
);
}
Thanks in advance for the help.

You should set the current-image-index by clicking the img.
<img onClick={()=>openModal(id)=} key={id} src={url.urls.thumb} alt='' />
function openModal(id) {
setCurrent(id);
setModalIsOpen(true);
}
or use the id / index from the props
const [current, setCurrent] = useState(id);

set current by passing image id
<img onClick={()=>openModal(id)} key={id} src={url.urls.thumb} alt='' />
function openModal(id) {
setCurrent(id);
setModalIsOpen(true);
}

Related

save all content clicked into state initial as array

i use usestate to create saveImages and setSaveImages, this initial as array, i want to each time i call the function, the content is different, so each time must to push to the array that info, but instead of do push, only replace the 1 position of array, doesnt add new position with different info. I don't know if I explain myself
const galery = useSelector((state) => state.search);
const dispatch = useDispatch();
const [saveImages, setSaveImages] = useState([]);
function savePhoto(e) {
e.preventDefault();
const { target } = e;
const content = target.photo.src;
setSaveImages(content)
console.log(content)
localStorage.setItem("img", saveImages);
dispatch(actionAddFavorite(content));
}
return(
<section className="w-full gap-0 sm:columns-2 md:columns-3 xl:columns-4 2xl:columns-5 3xl:columns-6">
{galery.map((item, index) => {
return (
<form onSubmit={savePhoto} key={index} className="relative">
<button className="bg-gray absolute left-5 top-3 shadow-md">
Guardar
</button>
<img
name="photo"
className="object-cover p-2"
src={item.urls.regular}
alt={item.alt_description}
/>
</form>
);
})}
</section>
)
You set your saveImages to contain "content", but what you want is to add "content" to existing saveImages array. Here is how you can do this:
setSaveImages(oldImages => {
return [...oldImages, content];
});
And here you can learn everything you need to know about state in react

Displaying a nested data with a conditional: How can I not display the data whose product's color is not according to the conditional?

I am trying to display all the quantity of colors that are more than 10. It does display it, however, this will also display even the products whose colors are less than 10. How do I fix this? Thank you.
codesandbox: https://codesandbox.io/s/products-0ccdhk?file=/src/App.js:752-1189
{product &&
product.map((item, i) => (
<ul key={i}>
<li>{item.id}</li>
<li>{item.prodName + " " + item.size}</li>
{Object.entries(item.colorMap).map((color) => (
<>
{color[1] > 10 && (
<>
{color[0]} - {color[1]}
</>
)}
</>
))}
</ul>
))}
Currently, this is what it looks like:
{product &&
product.map((item, i) => {
const obj = item.colorMap;
for (let x in obj) {
if (obj[x] > 10) {
return <li key={i}>{item.prodName + " " + item.size}</li>;
}
}
})}
Link CodeSandbox: https://codesandbox.io/s/products-forked-mtt94c?file=/src/App.js:752-1008
Because you used "=> ()" in map method, it returned whole items in your product array. I replaced it as "=> {}" and returned with a condition in "{}".
You can also write like this:
export default function App() {
const newProduct = product.filter((item) => {
return Object.values(item.colorMap).every((color) => color > 10);
});
return (
<ul className="App">
{newProduct &&
newProduct.map((item, i) => (
<li key={i}>{item.prodName + " " + item.size}</li>
))}
</ul>
);
}

Not working onClick scroll left or right products in React

I have a list of products displayed horizontally.
I need to make a working on click Left or Right btns, to scroll next product into view.
This is my scrollProducts function which not working:
const scrollProducts = isRight => {
const $document = document.querySelector(".prodcuts")
if (!$document) {
return
}
productsRef.current.scrollLeft = isRight
? productsRef.current.scrollLeft + $document.clientWidth
: productsRef.current.scrollLeft - $document.clientWidth
}
JSX part:
<section className="prodcuts" ref={productsRef}>
<div>
<button onClick={() => scrollProducts(false)}>Left</button>
<button onClick={() => scrollProducts(true)}>Right</button>
</div>
<Row styles={styles.productCards}>
{site.siteMetadata.data.map(product => (
<ProductCard key={product.name} {...product} />
))}
</Row>
</section>
Any advice?

how to run different array on my map function for my nav component

Pretty new at coding so sorry if my question looks ridiculous...
I am building a menu on my website which is divided in several categories.
In this example, we have Theory and Video categories ( Video only 1 level and Theory is going deeper on 2 levels).
The code below is working for only 1 category at time (thats why the comments).
I would like to ask you how to build a more generic function that can run whatever the array is (for the map function) and avoid this: "Cannot read property 'map' of undefined".
render() {
const theories = this.props.menuTheory;
const videos = this.props.menuVideos;
// const menuTheory = theories.map((theory, i) => (
//
// <div className="nav__category" key={i} onClick={() => this.onSelect(theory.category)}>
//
// <div className={this.state.isSelected === theory.category
// ? "nav__category__dropdown nav__category__dropdown--isSelected"
// : "nav__category__dropdown"}>
// <span className="nav__category__text">{theory.category}</span>
// <span className="checked"><img src={'../static/icons/nav__check.svg'}/></span>
// </div>
// <ul className={this.state.isExpanded === theory.category
// ? "nav__chapterBox"
// : "nav__chapterBox nav__chapterBox--isNotExpanded"}>
// {theory.chapters && theory.chapters.map((chapter, i) => <NavChapter key={i} id={chapter.objectId} title={chapter.name} onClick={() => this.onSelect1(chapter.objectId)}/>)}
// </ul>
// </div>
// ))
const menuVideo = videos.map((video, i) => (
<div className="nav__category" key={i} onClick={() => this.onSelect(video.category)}>
<div className={this.state.isSelected === video.category
? "nav__category__dropdown nav__category__dropdown--isSelected"
: "nav__category__dropdown"}>
<span className="nav__category__text">{video.category}</span>
<span className="checked"><img src={'../static/icons/nav__check.svg'}/></span>
</div>
</div>
))
return (
<nav className="nav__categoryBox">
{/* {menuTheory} */}
{menuVideo}
</nav>
)
}
Thanks.
Okay, so it sounds like you're looking for a way to do conditional rendering in React. What you need to do is add some state to the component. I've added showVideos as a bool (true|false). Use showVideos in the render method to determine which menu to show. You still create the menus using map, but in the return block, check this.state.showVideos to determine which content to return. To get this fully working, you'll also need to add a button that calls toggleMenu onClick that will update your state and switch which menu is being shown.
toggleMenu(){
this.setState({
showVideos: !this.state.showVideos
});
}
render() {
const theories = this.props.menuTheory;
const videos = this.props.menuVideos;
const menuTheory = theories.map((theory, i) => (
<div className="nav__category" key={i} onClick={() => this.onSelect(theory.category)}>
<div className={this.state.isSelected === theory.category ? "nav__category__dropdown nav__category__dropdown--isSelected" : "nav__category__dropdown"}>
<span className="nav__category__text">{theory.category}</span>
<span className="checked"><img src={'../static/icons/nav__check.svg'}/></span>
</div>
<ul className={this.state.isExpanded === theory.category ? "nav__chapterBox" : "nav__chapterBox nav__chapterBox--isNotExpanded"}>
{theory.chapters && theory.chapters.map((chapter, i) => <NavChapter key={i} id={chapter.objectId} title={chapter.name} onClick={() => this.onSelect1(chapter.objectId)}/>)}
</ul>
</div>
))
const menuVideo = videos.map((video, i) => (
<div className="nav__category" key={i} onClick={() => this.onSelect(video.category)}>
<div className={this.state.isSelected === video.category ? "nav__category__dropdown nav__category__dropdown--isSelected" : "nav__category__dropdown"}>
<span className="nav__category__text">{video.category}</span>
<span className="checked"><img src={'../static/icons/nav__check.svg'}/></span>
</div>
</div>
))
return (
<nav className="nav__categoryBox">
<button onClick={this.toggleMenu}>Toggle Menu</button>
{this.state.showVideos ? menuVideo : menuTheory}
</nav>
)
}

I think this is another React/JS context issue, how do I extract this variable?

I have an array[] of tracks that I receive from an API.
I pass it to a map function which will return a track for every track in tracks. I want to export a variable (Song) specific to that track to be be processed in my event handler as such. The only thing thats not working is the scope of song. I cant set the state of song in my map function or the component goes into an infinite rerender loop.
handleEnter(){
//I want to get the song into this context and play it here
this.props.mouseEnter();
}
handleLeave(){
//same for pausing
this.props.mouseLeave();
}
createTrack(track){
var song = new Audio([track.preview_url]);
return (
<div className="image" key={track.id}>
<img
className="img-circle"
src={track.album.images[0].url}
onMouseEnter={this.handleEnter.bind(this)}
onMouseLeave={this.handleLeave.bind(this)}
/>
<p className="showMe"><span>{track.name}</span></p>
</div>
);
}
getTracks(){
if(this.props.tracks) {
console.log(this.props.tracks);
return (
<div>{this.props.tracks.map(track => this.createTrack(track))}</div>
);
}
}
componentWillMount(){
this.props.fetchMessage();
}
render(){
return(
<div>{this.getTracks()}</div>
)
}
if you want to use .bind, you can send it to handleEnter and handleLeave.
handleEnter( trackID ) {
// trackID available here
}
createTrack(track){
var song = new Audio([track.preview_url]);
return (
<div className="image" key={track.id}>
<img
className="img-circle"
src={track.album.images[0].url}
onMouseEnter={this.handleEnter.bind( this, track.id )}
onMouseLeave={this.handleLeave.bind( this, track.id )}
/>
<p className="showMe"><span>{track.name}</span></p>
</div>
);
}
It's typically best practice to not use .bind in react since it creates a new function on every render. Rather, you should create a <Track /> component, pass it the track, then pass handleEnter and handleLeave as props.
const track = ( props ) => {
let { track, handleEnter, handleLeave } = props;
const onMouseEnter = () {
handleEnter( track.id );
}
const onMouseLeave = () {
handleLeave( track.id );
}
return (
<div className="image" key={track.id}>
<img
className="img-circle"
src={track.album.images[0].url}
onMouseEnter={ onMouseEnter }
onMouseLeave={ onMouseLeave }
/>
<p className="showMe">
<span>{track.name}</span>
</p>
</div>
);
};
then in your render, you'd map like you're doing and output <Track /> pure components instead of full-on components
Have a look at this. Hopefully it will solve your problem.
handleEnter(track, e){
// you will get the full track object and use the data
this.props.mouseEnter();
}
handleLeave(track, e){
// you will get the full track object and use the data
this.props.mouseLeave();
}
componentWillMount(){
this.props.fetchMessage();
}
render(){
const createTrack = (track, index) => {
var song = new Audio([track.preview_url]);
return (
<div className="image" key={'track-'+ index}>
<img
className="img-circle"
src={track.album.images[0].url}
onMouseEnter={this.handleEnter.bind(this, track)}
onMouseLeave={this.handleLeave.bind(this,track)}
/>
<p className="showMe"><span>{track.name}</span></p>
</div>
);
}
return(
<div>{this.props.tracks ? this.props.tracks.map(createTrack) : null }</div>
)
}

Categories