javascriptreactjsreact-imagereact-multi-carousel

React images caption error from api maybe to do with correct Index


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;

Solution

  • 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>
      }}
      //...
    />