通过更新重复使用的相同组件来反应自定义模态组件问题

React Custom Modal Component Problems With Updating Same Component Used Repeatedly

我构建了一个自定义模态框。

我希望它在打开时执行一项特定功能。当此模态为 opened/closed.

时,我希望切换 CSS class

如果我只在模板中插入一次这个组件,这就很好用了。但就我而言,我将其插入三次。通过使用 componentDidMount,我插入了一些应该切换 CSS class 的 JS。它不会为第一个或第二个模态做,它只会为第三个做。

代码已更新!

这是父组件:

import React from "react";
import ModalSmall from "./ModalSmall";
import ModalMedium from "./ModalMedium";
import ModalLarge from "./ModalLarge";
import "bootstrap/dist/css/bootstrap.css";
import "./styles.scss";

export default class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isModalSmallOpen: false,
      isModalMediumOpen: false,
      isModalLargeOpen: false
    };
  }

  toggleModalSmall = (e) => {
    e.preventDefault();
    this.setState((prev) => ({
      ...prev,
      isModalSmallOpen: !prev.isModalSmallOpen
    }));
  };

  toggleModalMedium = (e) => {
    e.preventDefault();
    this.setState((prev) => ({
      ...prev,
      isModalMediumOpen: !prev.isModalMediumOpen
    }));
  };

  toggleModalLarge = (e) => {
    e.preventDefault();
    this.setState((prev) => ({
      ...prev,
      isModalLargeOpen: !prev.isModalLargeOpen
    }));
  };

  render() {
    return (
      <div className="container">
        <div className="row">
          <div className="col">
            <h1>Hello Y'all!</h1>
            <p className="yo-green">My Modal Samples</p>

            <div className="row mt-5">
              <div className="col">
                <button
                  className="btn btn-primary"
                  onClick={this.toggleModalSmall}
                >
                  Modal Small
                </button>
              </div>
              <div className="col">
                <button
                  className="btn btn-primary"
                  onClick={this.toggleModalMedium}
                >
                  Modal Medium
                </button>
              </div>
              <div className="col">
                <button
                  className="btn btn-primary"
                  onClick={this.toggleModalLarge}
                >
                  Modal Large
                </button>
              </div>
            </div>
          </div>
        </div>
        <ModalSmall
          modalName="smallModal"
          modalTitle="Small Modal"
          modalBody="This is the small modal!"
          toggleModal={this.toggleModalSmall}
          modalOpen={this.state.isModalSmallOpen}
        />
        <ModalMedium
          modalName="mediumModal"
          modalTitle="Medium Modal"
          modalBody="This is the medium modal!"
          toggleModal={this.toggleModalMedium}
          modalOpen={this.state.isModalMediumOpen}
        />
        <ModalLarge
          modalName="largeModal"
          modalTitle="Large Modal"
          modalBody="This is the LARGE modal!"
          toggleModal={this.toggleModalLarge}
          modalOpen={this.state.isModalLargeOpen}
        />
      </div>
    );
  }
}

中间组件之一:

import React from "react";
import Modal from "./Modal";

const ModalSmall = (props) => {
  return (
    <Modal
      modalName={props.modalName}
      modalTitle={props.modalTitle}
      modalBody={props.modalBody}
      toggleModal={props.toggleModal}
      modalOpen={props.modalOpen}
    />
  );
};

export default ModalSmall;

这是我的模态组件:

import React from "react";

export default class Modal extends React.Component {
  componentDidUpdate() {
    if (this.props.modalOpen) {
      console.log("Open!", this.props.modalOpen);
      document.body.classList.add("drawer-open");
    } else {
      console.log("Closed!", this.props.modalOpen);
      document.body.classList.remove("drawer-open");
    }
  }

  render() {
    return (
      <div className="mymodal" id={this.props.modalName}>
        <div
          onClick={this.props.toggleModal}
          className={`mymodal-overlay ${this.props.modalOpen && "active"}`}
            ></div>
        <div
          className={`mymodal-content d-flex flex-column ${
            this.props.modalOpen && "active"
          }`}
        >
          <header className="p-2 border-bottom d-flex">
            <span
              className="material-icons clickable"
              onClick={this.props.toggleModal}
            >
              close
            </span>
            <div className="flex-grow-1 ml-2">{this.props.modalTitle}</div>
          </header>
          <div className="p-2 flex-grow-1">{this.props.modalBody}</div>
          <footer className="p-2 border-top">&copy; ChidoPrime 2021</footer>
        </div>
      </div>
    );
  }
}

Working Sample Here with Solution Applied

更新! --------------

我想包括第二种方法,不同于@sanishJoseph 提供的检查答案。我在其中添加了一个构造函数并在模态控制器中声明了一个状态。无需使用 React.PureComponent。我在 componentDidUpdate 中使用 preProvs。模态代码如下:

  constructor(props) {
    super(props);
    this.state = {
      modalOpen: false
    };
  }


  componentDidUpdate(prevProps) {
    if (prevProps.modalOpen === this.props.modalOpen) return;

    if (this.props.modalOpen) {
      console.log("Open!", this.props.modalOpen);
      document.body.classList.add("drawer-open");
    } else {
      console.log("Closed!", this.props.modalOpen);
      document.body.classList.remove("drawer-open");
    }
  }

Second Sample using prevProps without using React.PureComponent

代码理解起来有点复杂,但我认为主要问题在于用于实现它的逻辑。如果我理解正确的话,您不止一次使用同一个组件。因此,每个组件在每次重新渲染时都会执行 componentDidUpdate 方法。

这意味着如果您使用方法“toggleModal...”切换“父”组件中的模态框之一,则执行父渲染方法并执行每个渲染子方法。那里发生的事情是,在你的第一个模式中,你添加了 o 删除主体 css,在第二个模式中,你正在做相反的事情,在第三个模式中,你再次添加和删除。

你有很多事情可以在那里变得更好,但最简单的是使用你在你的 componentDidUpdated 方法中获得的参数,并确保你只在新道具发生变化时执行你的代码。这将解决您的问题。

我认为最大的错误在于您的 Parent 组件。您的页面初始状态是

 this.state = {
          isModalSmallOpen: false,
          isModalMediumOpen: false,
          isModalLargeOpen: false
        }

但是,当您打开模态框时,您将状态设置为该状态中的一项,其余项将为空。意思是,当你这样做时

this.setState({
      isModalSmallOpen: !this.state.isModalSmallOpen
    })

您正在设置 isModalMediumOpen: null, isModalLargeOpen: null

你应该做的是,

this.setState((prev) => ({...prev,
          isModalSmallOpen: !prev.isModalSmallOpen
        }))

所以你所有的州都将保留在你的州。所有 3 个模态打开功能都需要此更改。

更新:

修复非常简单。如果它是功能组件,您需要做的就是添加一个 react.memo。在您的情况下,将您的 Modal 组件设为 PureComponent。

导出默认 class 模态扩展 React.PureComponent

Pure Components in React are the components which do not re-renders when the value of state and props has been updated with the same values.

https://codesandbox.io/s/my-custom-modal-forked-yg4vo?file=/src/App.js