通过更新重复使用的相同组件来反应自定义模态组件问题
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">© 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
我构建了一个自定义模态框。
我希望它在打开时执行一项特定功能。当此模态为 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">© 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