reactjsimagereact-hooksreact-modalimage-slider

Dynamically rendering images into modal form mapped array of images


Im working on a project and only started recently with React.So far i made card component that is dynamically rendering data from js-file with a mapped function.One of the data being rendered are (stored in array) images where im displaying the first img and setting up a onClick function that opens the modal.

And this is where i'm stuck.Modal opens with non targeted img, where i would like to display the clicked image in modal first and pass the rest of the images of that array that can be clicked through.Like a image slider.When i console.log the images i can see im getting all the arrays in modal but i don't know how to set them accordingly.

I would appreciate some input how to go about the problem and what i am doing wrong.

I have a parent component which is receiving the data from js-file.

class Room extends Component{
  constructor(){
    super();
    this.state = {
      data: roomData,
    }
  }

    render(){
      return (
        <>
          <div>helo from room page</div>
          <RoomCards  data={this.state.data} />
        </>
        
      );
    }
}

this is the card Component

import Modal from "../components/modal";



class RoomCards extends Component{
constructor(){
  super();

  this.state = {
    showModal: false,
  
  };

}

getModal = ()=> {
  this.setState({ showModal: true })
}
hideModal = () => {
  this.setState({ showModal: false });
} 


render(){
  return (
    <>
      {
        this.props.data.map((data) => {
          return <div className="card__container" key={data.id} >
                    <div className="card">
                      <h2 className="card__title">{data.name}</h2>
                        <p className="card__description">{data.paragraph}</p>
                          <ul><li>{data.list[0]}</li></ul>
                          <ul><li>{data.list[1]}</li></ul>
                          <ul><li>{data.list[2]}</li></ul>
                          <ul><li>{data.list[2]}</li></ul>

                          <div  className="card__img">
                          <img src={data.image} 
                          onClick={() => this.getModal(data)} 
                          alt="" />

                          <Modal 
                            show={this.state.showModal}
                            onHide={() => this.hideModal()}
                            image={data.image} />
                          </div>
                    </div>
                    <button className="card__btn">Bestill overnatting</button>
                  </div>
          })  
        }
    </>
    )
  }
}
export default RoomCards

the modal component

import React, { Component } from "react";
import  "../styles/Room.scss";

class Modal extends Component {
  constructor(){
  super();

  this.state = {
      index:0
  };
}


  next =() => { 
    if({index : this.state.index}){
      this.setState({index: this.state.index + 1});
    }else {
      this.setState({index: this.state.index})
      console.log("heloo i am at index");
  }
}

  prev=()=>{
    if(this.state.index === 0 || this.state.index === -1) {
        console.log("hi")
      }else {
        this.setState({index :this.state.index - 1});
        }
    }



  render() {
    console.log(this.props.image)
    return (
      <React.Fragment>
        {this.props.show && (
          <div className="modal">
            <img alt="" src={this.props.image[this.state.index]} key={this.state.index} />
              {/* <img alt="" src={this.props.image[1]}/> */}
        
            <button onClick={this.props.onHide}>Close Modal</button>
            <button onClick={this.prev}>👈</button>
            <button onClick={this.next}>👉</button>  
          </div>
        )}
      </React.Fragment>
    );
  }
}

export default Modal;

and the data from js-file


const roomData = [
  {
    id:1,
    name: "standard room something new",
    paragraph:"Hotellets standard enkeltrom passer perfekt til deg som reiser alene. Rommene er møblert med enten en 90 cm seng eller en 160 cm bred seng, skrivebord, lenestol, og baderom med dusj ",
    list: ["wifi", "tv", "ikke royken"],
    image: ['https://images.unsplash.com/photo-1585255318859-f5c15f4cffe9?crop=entropy&cs=tinysrgb&fit=crop&fm=jpg&h=500&ixlib=rb-1.2.1&q=80&w=500',
            'https://images.unsplash.com/photo-1584226761916-3fd67ab5ac3a?crop=entropy&cs=tinysrgb&fit=crop&fm=jpg&h=500&ixlib=rb-1.2.1&q=80&w=500',
            'https://images.unsplash.com/photo-1585084335487-f653d0859c14?crop=entropy&cs=tinysrgb&fit=crop&fm=jpg&h=500&ixlib=rb-1.2.1&q=80&w=500',
            'https://images.unsplash.com/photo-1583217874534-581393fd5325?crop=entropy&cs=tinysrgb&fit=crop&fm=jpg&h=500&ixlib=rb-1.2.1&q=80&w=500',
          ]
  },
    
    {
    id:2,
    name: "standard room something new",
    paragraph:"Hotellets standard enkeltrom passer perfekt til deg som reiser alene. Rommene er møblert med enten en 90 cm seng eller en 160 cm bred seng, skrivebord, lenestol, og baderom med dusj ",
    list:  ["wifi","tv", "ikke royken"],
    image: ['https://images.unsplash.com/photo-1585179292338-45ba1f62f082?crop=entropy&cs=tinysrgb&fit=crop&fm=jpg&h=500&ixlib=rb-1.2.1&q=80&w=500',
            'https://images.unsplash.com/photo-1584753987666-ead137ec0614?crop=entropy&cs=tinysrgb&fit=crop&fm=jpg&h=500&ixlib=rb-1.2.1&q=80&w=500',
            'https://images.unsplash.com/photo-1583217874534-581393fd5325?crop=entropy&cs=tinysrgb&fit=crop&fm=jpg&h=500&ixlib=rb-1.2.1&q=80&w=500',]
  },
    {
    id:3,
    name: "standard room something new",
    paragraph:"Hotellets standard enkeltrom passer perfekt til deg som reiser alene. Rommene er møblert med enten en 90 cm seng eller en 160 cm bred seng, skrivebord, lenestol, og baderom med dusj ",
    list: ["wifi", "tv", "ikke..."],
    image:['https://images.unsplash.com/photo-1584691267914-91c0bee55964?crop=entropy&cs=tinysrgb&fit=crop&fm=jpg&h=500&ixlib=rb-1.2.1&q=80&w=500',
            'https://images.unsplash.com/photo-1585084335487-f653d0859c14?crop=entropy&cs=tinysrgb&fit=crop&fm=jpg&h=500&ixlib=rb-1.2.1&q=80&w=500']
  },
    {
    id:4,
    name: "standard room something new",
    paragraph:"Hotellets standard enkeltrom passer perfekt til deg som reiser alene. Rommene er møblert med enten en 90 cm seng eller en 160 cm bred seng, skrivebord, lenestol, og baderom med dusj ",
    list: ["wifi", "tv,"],
    image: ['https://images.unsplash.com/photo-1583217874534-581393fd5325?crop=entropy&cs=tinysrgb&fit=crop&fm=jpg&h=500&ixlib=rb-1.2.1&q=80&w=500']
  }
]
export default roomData;

Solution

  • The main issue here is that you are rendering a modal for each data element mapped but using a single showModal state that ends up toggling all of them open/close together.

    I suggest storing the the specific data you want to display in the modal into the state.showModal state, with an initial value null. Conditionally render a single Modal component.

    class RoomCards extends Component {
      state = {
        showModal: null
      };
    
      getModal = (data) => {
        this.setState({ showModal: data });
      };
      hideModal = () => {
        this.setState({ showModal: null });
      };
    
      render() {
        return (
          <>
            {this.props.data.map((data) => {
              return (
                <div className="card__container" key={data.id}>
                  <div className="card">
                    <h2 className="card__title">{data.name}</h2>
                    <p className="card__description">{data.paragraph}</p>
                    <ul>
                      {data.list.map((item) => (
                        <li key={item}>{item}</li>
                      ))}
                    </ul>
    
                    <div className="card__img">
                      <img
                        src={data.image}
                        onClick={() => this.getModal(data)}
                        alt=""
                      />
                    </div>
                  </div>
                  <button className="card__btn">Bestill overnatting</button>
                </div>
              );
            })}
    
            {this.state.showModal && (
              <Modal
                onHide={this.hideModal}
                images={this.state.showModal?.image}
              />
            )}
          </>
        );
      }
    }
    

    Fix up the modal to maintain valid current index value.

    class Modal extends Component {
      state = {
        index: 0
      };
    
      next = () => {
        const { images } = this.props;
        this.setState(({ index }) => ({
          index: Math.min(images.length - 1, index + 1)
        }));
      };
    
      prev = () => {
        this.setState(({ index }) => ({
          index: Math.max(0, index - 1)
        }));
      };
    
      render() {
        const { index } = this.state;
        const { images, onHide } = this.props;
        return (
          <div className="modal">
            <img alt="" src={images[index]} />
    
            <button onClick={this.props.onHide}>Close Modal</button>
            <button onClick={this.prev}>👈</button>
            <button onClick={this.next}>👉</button>
          </div>
        );
      }
    }
    

    Edit dynamically-rendering-images-into-modal-form-mapped-array-of-images