1

I have a table rows data from server containing images (some other data is removed for simplicity). When images clicked, a modal popup is shown to preview the loaded image to crop and change the image with the cropped one. Everything is work fine.

The problem is, the clicked image on the row should change after the modal submit button is clicked. But I found that the image on the last row is changed.

I know the problem comes from this line but I have no idea how to solve it :

handleSubmit = e => {
    e.preventDefault();

    console.log(this.state.croppedImageUrl);

    this.imagetoCropt.src = this.state.croppedImageUrl;

  };

This is the code :

import React, { Component } from "react";
import { Link } from "react-router-dom";
import { Button } from "react-bootstrap";
import { Modal } from "react-bootstrap";
import ReactCrop from "react-image-crop";
import "react-image-crop/dist/ReactCrop.css";

import { my_ads } from "./component/AdsFunctions";


export default class Myads extends Component {

 constructor() {
    super();

    this.state = {
      myads : {},
      modalShow: false,
      setShow: false,
      setClose: true,
      previewImage: "/assets/loader.gif",
      src: null,
      crop: {
        unit: "%",
        width: 30,
        aspect: 5 / 4
      }
    };
  }

  handleImageOnChange = e => {
    if (e.target.files && e.target.files.length > 0) {
      const reader = new FileReader();
      reader.addEventListener("load", () =>
        this.setState({
          src: reader.result,
          modalShow: true
        })
      );
      reader.readAsDataURL(e.target.files[0]);
    }
  };

  onImageLoaded = image => {
    this.imageRef = image;
  };

  onCropComplete = crop => {
    this.makeClientCrop(crop);
  };

  onCropChange = (crop, percentCrop) => {
    this.setState({ crop });
  };

  async makeClientCrop(crop) {
    if (this.imageRef && crop.width && crop.height) {
      const croppedImageUrl = await this.getCroppedImg(
        this.imageRef,
        crop,
        "newFile.jpeg"
      );
      this.setState({ croppedImageUrl });
    }
  }

  getCroppedImg(image, crop, fileName) {
    const canvas = document.createElement("canvas");
    const scaleX = image.naturalWidth / image.width;
    const scaleY = image.naturalHeight / image.height;
    canvas.width = crop.width;
    canvas.height = crop.height;
    const ctx = canvas.getContext("2d");

    ctx.drawImage(
      image,
      crop.x * scaleX,
      crop.y * scaleY,
      crop.width * scaleX,
      crop.height * scaleY,
      0,
      0,
      crop.width,
      crop.height
    );

    return new Promise((resolve, reject) => {
      canvas.toBlob(blob => {
        if (!blob) {
          //reject(new Error('Canvas is empty'));
          console.error("Canvas is empty");
          return;
        }
        blob.name = fileName;
        window.URL.revokeObjectURL(this.fileUrl);
        this.fileUrl = window.URL.createObjectURL(blob);
        resolve(this.fileUrl);
      }, "image/jpeg");
    });
  }

  //---- modal function ------------
  handleShow = () => {
    this.setState({
      modalShow: true
    });
  };

  handleClose = () => {
    this.setState({
      modalShow: false
    });
  };

  handleImgClick = () => {
    this.refs.fileInput.click();
  };

  handleClickSubmit = () => {
    this.refs.btnSubmit.click();
    this.setState({
      modalShow: false
    });
  };

  //--------- end modal function---


//======== PROBLEM HERE ======================
   handleSubmit = e => {
    e.preventDefault();

    console.log(this.state.croppedImageUrl);

    this.imagetoCropt.src = this.state.croppedImageUrl;

  };
//=============================================

 componentDidMount() {
   // AXIOS call
    my_ads().then(res => {

        this.setState({  
          myads: res.myads,
        });
    });     
  }

  render() {

    const { crop, croppedImageUrl, src } = this.state;
    const show = this.state.modalShow;

    // My Ads List from AXIOS call 
    let myads    = this.state.myads;

     const RenderMyAds = Object.keys(myads).map((val, index) => (
        <tr className="mt-3" key={index}>
          <td>
            <div className="float-left mr-4">
              <div className="card mb-10">
                <Link to="#">
                  <img
                    className="img-thumbnail img-responsive"
                    src={myads[val].image}
                    alt="img"
                    width={200}
                    onClick={this.handleImgClick}
                    ref={ref => (this.imagetoCropt = ref)} <<==== problem here?
                  />
                </Link>
              </div>
            </div>
          </td>
        </tr>
      ));


    return (
      <div>     
        <section>
          <div className="container">
            <div className="row">
              <div className="col-lg-12">
                <div className="card">

                  <div className="card-body">
                    <div className="table-responsive">
                      <table className="table table-bordered border-top mb-0">
                        <tbody>

                           {RenderMyAds}

                        </tbody>
                      </table>
                    </div>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </section>

        <form
          encType="multipart/form-data"
          acceptCharset="utf-8"
          onSubmit={this.handleSubmit}
        >
          <input
            type="file"
            className="d-none"
            name="userfile"
            ref="fileInput"
            onChange={this.handleImageOnChange}
          />
          <button type="submit" className="d-none" ref="btnSubmit">
            Upload Image
          </button>
        </form>


        <Modal size="lg" show={show} onHide={this.handleClose}>
          <Modal.Header closeButton>
            <Modal.Title>Image Preview</Modal.Title>
          </Modal.Header>
          <Modal.Body className="text-center"></Modal.Body>
          <ReactCrop
            src={src}
            crop={crop}
            onImageLoaded={this.onImageLoaded}
            onComplete={this.onCropComplete}
            onChange={this.onCropChange}
          />

          <img className="d-none" alt="Crop" src={croppedImageUrl} />
          <Modal.Footer>
            <Button
              variant="primary"
              className="btn-block"
              onClick={this.handleClickSubmit}
            >
              <i className="fa fa-image mr-2"></i> Upload Image
            </Button>
          </Modal.Footer>
        </Modal>
      </div>
    );
  }
}

1 Answer 1

1

You are overwriting the same ref in your map. Consequentially, the last row is the last one to be mapped. You need to instead use an array of refs.

In your contructor, add:

this.imageRefs = [];

Then in your mapping:

const RenderMyAds = Object.keys(myads).map((val, index) => (
    <tr className="mt-3" key={index}>
        <td>
        <div className="float-left mr-4">
            <div className="card mb-10">
            <Link to="#">
                <img
                className="img-thumbnail img-responsive"
                src={myads[val].image}
                alt="img"
                width={200}
                onClick={this.handleImgClick}
                ref={ref => (this.imageRefs[index] = ref)}
                />
            </Link>
            </div>
        </div>
        </td>
    </tr>
));

This will let you access the correct ref, based on the key assigned to the tr.

Sign up to request clarification or add additional context in comments.

4 Comments

@Jeremi What to do on handleSubmit? this.imageRefs.src = this.state.croppedImageUrl; not working
In handleImgClick, set the index of the ref you need on the object, like this.selectedImageRef = (code here to get ref) and then you will have access to it in the submission.
Still can not get the index value. In handleImgClick try to alert(e.target.ref) return undefined. Please help me more. Tx
Problem solved by adding data-id={myads[val].id} and ref={ref => (this.imageRefs[myads[val].id] = ref)} in image. And this.selectedImageRef =e.target.getAttribute('data-id') In handleImgClick

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.