简体   繁体   中英

Callback function, responsible for updating state, passed as props to child component not triggering a state update

The callback function (lies in Images component) is responsible for making a state update. I'm passing that function as props to the Modal component, and within it it's being passed into the ModalPanel component.

That function is used to set the state property, display, to false which will close the modal. Currently, that function is not working as intended.

Image Component:

class Images extends Component {
  state = {
    display: false,
    activeIndex: 0
  };

  handleModalDisplay = activeIndex => {
    this.setState(() => {
      return {
        activeIndex,
        display: true
      };
    });
  };

  closeModal = () => {
    this.setState(() => {
      return { display: false };
    });
  }

  render() {
    const { imageData, width } = this.props;
    return (
      <div>
        {imageData.resources.map((image, index) => (
          <a
            key={index}
            onClick={() => this.handleModalDisplay(index)}
          >
            <Modal
              closeModal={this.closeModal}
              display={this.state.display}
              activeIndex={this.state.activeIndex}
              selectedIndex={index}
            >
              <Image
                cloudName={CLOUDINARY.CLOUDNAME}
                publicId={image.public_id}
                width={width}
                crop={CLOUDINARY.CROP_TYPE}
              />
            </Modal>
          </a>
        ))}
      </div>
    );
  }
}

export default Images;

Modal Component:

const overlayStyle = {
  position: 'fixed',
  zIndex: '1',
  paddingTop: '100px',
  left: '0',
  top: '0',
  width: '100%',
  height: '100%',
  overflow: 'auto',
  backgroundColor: 'rgba(0,0,0,0.9)'
};

const button = {
  borderRadius: '5px',
  backgroundColor: '#FFF',
  zIndex: '10'
};

class ModalPanel extends Component {
  render() {
    const { display } = this.props;
    console.log(display)
    const overlay = (
      <div style={overlayStyle}>
        <button style={button} onClick={this.props.closeModal}>
          X
        </button>
      </div>
    );
    return <div>{display ? overlay : null}</div>;
  }
}

class Modal extends Component {
  render() {
    const {
      activeIndex,
      children,
      selectedIndex,
      display,
      closeModal
    } = this.props;
    let modalPanel = null;
    if (activeIndex === selectedIndex) {
      modalPanel = (
        <ModalPanel display={this.props.display} closeModal={this.props.closeModal} />
      );
    }

    return (
      <div>
        {modalPanel}
        {children}
      </div>
    );
  }
}

export default Modal;

links to code https://github.com/philmein23/chez_portfolio/blob/chez_portfolio/components/Images.js

https://github.com/philmein23/chez_portfolio/blob/chez_portfolio/components/Modal.js

You're dealing with this modal through a very non-react and hacky way.

Essentially, in your approach, all the modals are always there, and when you click on image, ALL modals display state becomes true, and you match the index number to decide which content to show. I suspect it's not working due to the multiple children of same key in Modal or Modal Panel.

I strongly suggest you to ditch current approach. Here's my suggestions:

  1. Only a single <Modal/> in Images component.
  2. Add selectedImage state to your Images component. Every time you click on an image, you set selectedImage to that clicked image object.
  3. Pass selectedImage down to Modal to display the content you want.

This way, there is only ONE modal rendered at all time. The content changes dynamically depending on what image you click.

This is the working code I tweaked from your repo:

(I'm not sure what to display as Modal content so I display public_id of image)

Images Component

class Images extends Component {
  state = {
    display: false,
    selectedImage: null
  };

  handleModalDisplay = selectedImage => {
    this.setState({
      selectedImage,
      display: true
    })
  };

  closeModal = () => {
    //shorter way of writing setState
    this.setState({display: false})
  }

  render() {
    const { imageData, width } = this.props;
    return (
      <div>
        <Modal
          closeModal={this.closeModal}
          display={this.state.display}
          selectedImage={this.state.selectedImage}
        />
        {imageData.resources.map((image, index) => (
          <a
            //Only use index as key as last resort
            key={ image.public_id }
            onClick={() => this.handleModalDisplay(image)}
          >
            <Image
              cloudName={CLOUDINARY.CLOUDNAME}
              publicId={image.public_id}
              width={width}
              crop={CLOUDINARY.CROP_TYPE}
            />
          </a>
        ))}
      </div>
    );
  }
}

Modal Component

class Modal extends Component {
  render() {
    const { display, closeModal, selectedImage } = this.props;

    const overlayContent = () => {
      if (!selectedImage) return null; //for when no image is selected
      return (
        //Here you dynamically display the content of modal using selectedImage
        <h1 style={{color: 'white'}}>{selectedImage.public_id}</h1>
      )
    }

    const overlay = (
      <div style={overlayStyle}>
        <button style={button} onClick={this.props.closeModal}>
          X
        </button>
        {
          //Show Modal Content
          overlayContent()
        }
      </div>
    );
    return <div>{display ? overlay : null}</div>;
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM