2

Following on from: How to make my react-multi-carousel use a light-box feature for my ReactJS app

My app is using react-images for lightbox and react-carousel-images for the carousel. My api has a title and image. Problem I have is the title in the footer caption is always coming as the image selected and not changing when in the lightbox carousel. I think it is to do with the index of the selected image? My code so far is:

example of issue: https://codesandbox.io/s/react-multi-corousal-issue-72s9o?file=/src/Slider.js

slider.js

import React, { Component } from "react";
// import "../slider/slider.css";
import "./slider.css";
import Carousel from "react-multi-carousel";
import "react-multi-carousel/lib/styles.css";
import LightBox, { Modal, ModalGateway } from "react-images";

const responsive = {
  superLargeDesktop: {
    breakpoint: { max: 4000, min: 3000 },
    items: 1
  },
  desktop: {
    breakpoint: { max: 3000, min: 1024 },
    items: 1
  },
  tablet: {
    breakpoint: { max: 1024, min: 464 },
    items: 1
  },
  mobile: {
    breakpoint: { max: 464, min: 0 },
    items: 1
  }
};

class Slider extends Component {
  _isMounted = false;

  state = {
    awsApiData: [],
    loading: false,
    //selectedIndex: 0,
    selectedImage: {},
    lightboxIsOpen: false
  };

  componentDidMount() {
    this._isMounted = true;
    console.log("app mounted");
    this.setState({ loading: true });
    /*global fetch */
    fetch("https://onelbip0e6.execute-api.eu-west-2.amazonaws.com/livestage/imgApi")
      .then(data => data.json())
      .then(data =>
        // this.setState({ awsApiData: data[0], loading: false }, () =>
        this.setState(
          {
            // awsApiData: data.map(item => ({source: item.download_url })),
            awsApiData: data.map(item => ({
              ...item,
              source: item.image
            })),
            loading: false
          },
          () => console.log(data)
        )
      );
  }
  componentWillUnmount() {
    this._isMounted = false;
  }

  toggleLightbox = (post, selectedIndex) => {
    // this.setState(state => ({
    //   lightboxIsOpen: !state.lightboxIsOpen,
    //   selectedIndex
    // }));
    this.setState(state => ({
      lightboxIsOpen: !state.lightboxIsOpen,
      selectedImage: { title: post.title, index: selectedIndex }
    }));
  };

  render() {
    return (
      <div>
        {this.state.loading ? (
          <div className="text-center">Loading...</div>
        ) : (
          <div>
            <Carousel
              additionalTransfrom={0}
              showDots={false}
              arrows={true}
              autoPlaySpeed={3000}
              autoPlay={true}
              centerMode={false}
              className="slider"
              containerClass="container-with-dots"
              dotListClass="dots"
              draggable
              focusOnSelect={false}
              infinite
              itemClass="carousel-top"
              keyBoardControl
              minimumTouchDrag={80}
              renderButtonGroupOutside={false}
              renderDotsOutside
              responsive={responsive}
            >
              {Object.values(this.state.awsApiData).map((post, indx) => {
                return (
                  <div
                    className="mt-5"
                    key={indx}
                    //onClick={() => this.toggleLightbox(indx)}
                    onClick={() => this.toggleLightbox(post, indx)}

                  >
                    <img
                      className="media-img card-img-top card-img-hero"
                      src={post.source}
                      alt="Alt text"
                      style={{ cursor: "pointer" }}
                    />
                  </div>
                );
              })}
            </Carousel>
            <ModalGateway>
              {this.state.lightboxIsOpen ? (
                <Modal onClose={this.toggleLightbox}>
                  <LightBox
                    components={{
                      FooterCaption: () => <div>{this.state.selectedImage.title}</div>
                    }}
                    //currentIndex={this.state.selectedIndex}
                   currentIndex={this.state.selectedImage.index}
                    // formatters={{ getAltText }}
                    frameProps={{ autoSize: "height" }}
                    views={this.state.awsApiData}
                  />
                </Modal>
              ) : null}
            </ModalGateway>
          </div>
        )}
      </div>
    );
  }
}

export default Slider;
Mosh Feu
  • 28,354
  • 16
  • 88
  • 135
Sole
  • 3,100
  • 14
  • 58
  • 112

1 Answers1

2

Your caption is set to this.state.selectedImage.title but you never update the state (i.e. call setState()) when your light box view changes.

You need to add the onViewChange handler to the LightBox component:

<LightBox onViewChange={handleLightBoxViewChange} .../>

That handler will receive the index of the item the view changes too. In handleLightBoxViewChange call setState to update selectedImage:

handleLightBoxViewChange = (newIndex) => {
  this.setState(state => ({
    selectedImage: state.awsApiData[newIndex]
  }));
};

If you don't wish to update the state, your footer component receives the current view so you could update it like this:

<LightBox
  components={{
    FooterCaption: ({currentView}) => <div>{currentView.title}</div>
  }}
  //...
/>
rojoca
  • 11,040
  • 4
  • 45
  • 46
  • Thanks either of those options worked. Is there a preferred way to do this i.e update state or just do it via the view? – Sole Apr 17 '20 at 01:56
  • @Sole I don't think there is a preferred way, you just need to decide which suits your needs the best. Updating the `FooterCaption` is the simplest, which is generally a good way to go. – rojoca Apr 17 '20 at 03:07