Mid-Autumn Festival holiday, a little boring, so write some blog warm heart, at the same time I wish you a happy Mid-Autumn Festival ~ 🙃
The next step is to walk you through the implementation of a basic modal popover component, encapsulating a simple animation component, and annotating some of the elements involved in the code.
I. Implementation of modal components;
1. Environment construction
We use the create-react-app command to quickly set up the development environment:
create-react-app modal
Copy the code
After the installation is complete, start the project as prompted, and then create a modal directory under SRC and modal.jsx modal.css
Modal.jsx has the following contents:
import React, { Component } from 'react';
import './modal.css';
class Modal extends Component {
render() {
return <div className="modal">This is a modal component</div>}}export default Modal;
Copy the code
Go back to the root directory, open app.js, and replace the contents with the following:
import Modal from './modal/modal';
import React, { Component } from 'react';
import './App.css';
class App extends Component {
render() {
return <div className="app">
<Modal></Modal>
</div>}}export default App;
Copy the code
After completing the above steps, our browser should look like this:
2. Perfect modal style
So before we do that, let’s think about what we normally use in a modal component: a header area, a content area, a control area, a mask;
Modal.jsx contents are modified as follows:
import React, { Component } from 'react';
import './modal.css';
class Modal extends Component {
render() {
return <div className="modal-wrapper">
<div className="modal">
<div className="modal-title">This is a modal heading</div>
<div className="modal-content">This is modal content</div>
<div className="modal-operator">
<button className="modal-operator-close">cancel</button>
<button className="modal-operator-confirm">confirm</button>
</div>
</div>
<div className="mask"></div>
</div>}}export default Modal;
Copy the code
The modal. CSS content is modified as follows:
.modal {
position: fixed;
width: 300px;
height: 200px;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
border-radius: 5px;
background: #fff;
overflow: hidden;
z-index: 9999;
box-shadow: inset 0 0 1px 0 # 000;
}
.modal-title {
width: 100%;
height: 50px;
line-height: 50px;
padding: 0 10px;
}
.modal-content {
width: 100%;
height: 100px;
padding: 0 10px;
}
.modal-operator {
width: 100%;
height: 50px;
}
.modal-operator-close..modal-operator-confirm {
width: 50%;
border: none;
outline: none;
height: 50px;
line-height: 50px;
opacity: 1;
color: #fff;
background: rgb(247, 32, 32).cursor: pointer;
}
.modal-operator-close:active..modal-operator-confirm:active {
opacity:.6;
transition: opacity .3s;
}
.mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: # 000;
opacity:.6;
z-index: 9998;
}
Copy the code
After the modification is completed, our browser will display the following picture:
3. Development of modal function
Now that we’re ready to do that, let’s go ahead and implement modal, and again, when we use modal components, what are the basic functions?
- Can be achieved by
visible
controlmodal
Show hidden; title
.content
You can customize the display content;- I’m going to cancel and close
modal
Is also called by the nameonClose
, clicking ok will invoke the nameconfirm
And closemodal
, click on the maskmask
Shut downmodal
; - The animate field turns animation on/off.
Add 3.1.visible
Field controls explicit and implicit
Modal.jsx is modified as follows:
import React, { Component } from 'react';
import './modal.css';
class Modal extends Component {
constructor(props) {
super(props)
}
render() {
// The visile control passed through the parent component is explicit or implicit
const { visible } = this.props;
return visible && <div className="modal-wrapper">
<div className="modal">
<div className="modal-title">This is a modal heading</div>
<div className="modal-content">This is modal content</div>
<div className="modal-operator">
<button className="modal-operator-close">cancel</button>
<button className="modal-operator-confirm">confirm</button>
</div>
</div>
<div className="mask"></div>
</div>}}export default Modal;
Copy the code
App.js is modified as follows:
import Modal from './modal/modal';
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor(props) {
super(props)
// This is one of the binding methods. Since the method in the class is not automatically bound to the current example, we need to bind it manually. Otherwise, this would be undefined in the method.
// The second method is to use the arrow function, such as: showModal = () => {}
// The third method is to bind when called, such as this.showmodal.bind (this).
this.showModal = this.showModal.bind(this)
this.state = {
visible: false
}
}
showModal() {
this.setState({ visible: true })
}
render() {
const { visible } = this.state
return <div className="app">
<button onClick={this.showModal}>click here</button>
<Modal visible={visible}></Modal>
</div>}}export default App;
Copy the code
Above, we passed the visible state in the parent component app.js to the modal component, and then controlled the visible value through the button click event to achieve the effect of controlling the visible and implicit modal component
The effect of unclicked button is shown below:
Click the button and the effect is as follows:
3.2. title
withcontent
Content customization
Modal.jsx is modified as follows:
import React, { Component } from 'react';
import './modal.css';
class Modal extends Component {
constructor(props) {
super(props)
}
render() {
const { visible, title, children } = this.props;
return visible && <div className="modal-wrapper">
<div className="modal">{/* here use the parent's title*/}<div className="modal-title">{title}</div>{/* here the content uses the children of the parent */}<div className="modal-content">{children}</div>
<div className="modal-operator">
<button className="modal-operator-close">cancel</button>
<button className="modal-operator-confirm">confirm</button>
</div>
</div>
<div className="mask"></div>
</div>}}export default Modal;
Copy the code
App.js is modified as follows:
import Modal from './modal/modal';
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor(props) {
super(props)
this.showModal = this.showModal.bind(this)
this.state = {
visible: false
}
}
showModal() {
this.setState({ visible: true })
}
render() {
const { visible } = this.state
return <div className="app">
<button onClick={this.showModal}>click here</button>
<Modal
visible={visible}
title="Here's a custom title."
>This is the custom content</Modal>
</div>}}export default App;
Copy the code
Then we click the button on the page and the result is as follows:
3.3. Cancel and confirm buttons and add the mask click function
Think before you write: We need to close modal by clicking the cancel button, so we need to maintain a state in modal, and then use that state to control the visible and implicit of modal, which seems to work, but let’s think again, we used the visible of the parent component to control the visible of modal, so doesn’t that contradict us? That doesn’t work, so let’s make a change, so if the state of the parent changes, then we only update that state, and we click cancel in modal and we only update that state, and then we use that state value to control the implicit and explicit of modal; As for the onClose hook function, we can call it before updating the state, confirming the click of the button and canceling it.
Modal.jsx is modified as follows:
import React, { Component } from 'react';
import './modal.css';
class Modal extends Component {
constructor(props) {
super(props)
this.confirm = this.confirm.bind(this)
this.maskClick = this.maskClick.bind(this)
this.closeModal = this.closeModal.bind(this)
this.state = {
visible: false}}// The first rendering uses the parent component's state to update the visible state in modal, which is called only once
componentDidMount() {
this.setState({ visible: this.props.visible })
}
// When you receive props, you update the visible state in modal according to the parent component's state
componentWillReceiveProps(props) {
this.setState({ visible: props.visible })
}
// Click to cancel the visible state in modal
closeModal() {
console.log('Hello, my name is Cancel, heard you want to order me? Tsundere face 👸 ')
const { onClose } = this.props
onClose && onClose()
this.setState({ visible: false })
}
confirm() {
console.log('Hello everyone, my name is Confirm, upstairs cancel is my son, a little brain ~')
const { confirm } = this.props
confirm && confirm()
this.setState({ visible: false })
}
maskClick() {
console.log('Hello, I'm Mask, I've been clicked')
this.setState({ visible: false})
}
render() {
// Use the visible state maintained in modal to control visibility and invisibility
const { visible } = this.state;
const { title, children } = this.props;
return visible && <div className="modal-wrapper">
<div className="modal">
<div className="modal-title">{title}</div>
<div className="modal-content">{children}</div>
<div className="modal-operator">
<button
onClick={this.closeModal}
className="modal-operator-close"
>cancel</button>
<button
onClick={this.confirm}
className="modal-operator-confirm"
>confirm</button>
</div>
</div>
<div
className="mask"
onClick={this.maskClick}
></div>
</div>}}export default Modal;
Copy the code
App.js is modified as follows:
import Modal from './modal/modal';
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor(props) {
super(props)
this.confirm = this.confirm.bind(this)
this.showModal = this.showModal.bind(this)
this.closeModal = this.closeModal.bind(this)
this.state = {
visible: false
}
}
showModal() {
this.setState({ visible: true })
}
closeModal() {
console.log('I'm an onClose callback')
}
confirm() {
console.log('THIS is confirm callback')
}
render() {
const { visible } = this.state
return <div className="app">
<button onClick={this.showModal}>click here</button>
<Modal
visible={visible}
title="Here's a custom title."
confirm={this.confirm}
onClose={this.closeModal}
>This is the custom content</Modal>
</div>}}export default App;
Copy the code
After saving, click cancel and confirm in the browser, and the console will appear as shown in the picture below:
4. Modal optimization
So that’s a basic modal component, but we have a problem with that, which is that the modal component that we’re introducing now is in an element called App, and some of the most widely used UI frameworks do have modal components in the Body layer, wherever you introduce them, This prevents the modal component from being interfered with by the parent’s style.
“To do this, we had to understand React’s own nature: Portals. This feature is added after the 16th version, and before 16 version, is through the use of ReactDOM unstable_renderSubtreeIntoContainer method processing, this method can render elements to the specified element, React-redux communicates across components based on context, so ReactDOM. Render will cause context loss. This invalidates all context-based frameworks for cross-component communication.
4.1. ReactDOM.unstable_renderSubtreeIntoContainer
The use of
ReactDOM.unstable_renderSubtreeIntoContainer(
parentComponent, // To specify the context
element, // The element to render
containerNode, // Render to the specified DOM
callback / / callback
);
Copy the code
Oldportal. JSX = oldPortal. JSX = oldPortal. JSX = oldPortal. JSX = oldPortal. JSX
import React from 'react';
import ReactDOM from 'react-dom';
class OldPortal extends React.Component {
constructor(props) {
super(props)
}
// Initialize the visible property to determine whether to render
componentDidMount() {
const { visible } = this.props
if (visible) {
this.renderPortal(this.props); }}// Render and uninstall props each time it receives props
componentWillReceiveProps(props) {
if (props.visible) {
this.renderPortal(props)
} else {
this.closePortal()
}
}
/ / rendering
renderPortal(props) {
if (!this.node) {
// Prevent multiple node creation
this.node = document.createElement('div');
}
// Add current node to body
document.body.appendChild(this.node);
ReactDOM.unstable_renderSubtreeIntoContainer(
this.// Context specifies the current instance
props.children, // The rendered element is the current children
this.node, // Render the element into our new Node without using the fourth parameter callback.
);
}
/ / unloading
closePortal() {
if (this.node) {
// Unload components from the element
ReactDOM.unmountComponentAtNode(this.node)
// Remove the element
document.body.removeChild(this.node)
}
}
render() {
return null; }}export default OldPortal
Copy the code
After saving, we use it in modal.jsx:
import React, { Component } from 'react';
import OldPortal from '.. /oldPortal/oldPortal';
import './modal.css';
class Modal extends Component {
constructor(props) {
super(props)
this.confirm = this.confirm.bind(this)
this.maskClick = this.maskClick.bind(this)
this.closeModal = this.closeModal.bind(this)
this.state = {
visible: false
}
}
componentDidMount() {
this.setState({ visible: this.props.visible })
}
componentWillReceiveProps(props) {
this.setState({ visible: props.visible })
}
closeModal() {
console.log('Hello, my name is Cancel, heard you want to order me? Tsundere face 👸 ')
const { onClose } = this.props
onClose && onClose()
this.setState({ visible: false })
}
confirm() {
console.log('Hello everyone, my name is Confirm, upstairs cancel is my son, a little brain ~')
const { confirm } = this.props
confirm && confirm()
this.setState({ visible: false })
}
maskClick() {
console.log('Hello, I'm Mask, I've been clicked')
this.setState({ visible: false })
}
render() {
const { visible } = this.state;
const { title, children } = this.props;
return <OldPortal visible={visible}>
<div className="modal-wrapper">
<div className="modal">
<div className="modal-title">{title}</div>
<div className="modal-content">{children}</div>
<div className="modal-operator">
<button
onClick={this.closeModal}
className="modal-operator-close"
>cancel</button>
<button
onClick={this.confirm}
className="modal-operator-confirm"
>confirm</button>
</div>
</div>
<div
className="mask"
onClick={this.maskClick}
></div>
</div>
</OldPortal>}}export default Modal;
Copy the code
As you can see, we just wrap a layer of OldPortal component around the return content in modal, and then pass the state visible that controls explicit and implicit to OldPortal component, OldPortal actually controls the explicit and implicit of modal. Then we clicked on the button in the page and opened the console at the same time, and found modal as we thought, the bed was sent to the body layer:
Version 4.2. 16Portal
use
In version 16, react-DOM native provides a method, reactdom.createPortal (), to implement portal functionality:
ReactDOM.createPortal(
child, // The element to render
container // Specify the parent element to render
)
Copy the code
Reduce the two parameters than unstable_renderSubtreeIntoContainer, then we use it in the project.
In the SRC directory newPortal new directory in which the new newPortal. JSX, newPortal. JSX content is as follows:
import React from 'react';
import ReactDOM from 'react-dom';
class NewPortal extends React.Component {
constructor(props) {
super(props)
// Initialize the parent element that created the render and add it under body
this.node = document.createElement('div');
document.body.appendChild(this.node);
}
render() {
const { visible, children } = this.props;
// Direct explicit implicit representation
return visible && ReactDOM.createPortal(
children,
this.node, ); }}export default NewPortal
Copy the code
Can see clear content contrast unstable_renderSubtreeIntoContainer implementation simplifies a lot, then we in modal. JSX used in:
import React, { Component } from 'react';
import NewPortal from '.. /newPortal/newPortal';
import './modal.css';
class Modal extends Component {
constructor(props) {
super(props)
this.confirm = this.confirm.bind(this)
this.maskClick = this.maskClick.bind(this)
this.closeModal = this.closeModal.bind(this)
this.state = {
visible: false
}
}
componentDidMount() {
this.setState({ visible: this.props.visible })
}
componentWillReceiveProps(props) {
this.setState({ visible: props.visible })
}
closeModal() {
console.log('Hello, my name is Cancel, heard you want to order me? Tsundere face 👸 ')
const { onClose } = this.props
onClose && onClose()
this.setState({ visible: false })
}
confirm() {
console.log('Hello everyone, my name is Confirm, upstairs cancel is my son, a little brain ~')
const { confirm } = this.props
confirm && confirm()
this.setState({ visible: false })
}
maskClick() {
console.log('Hello, I'm Mask, I've been clicked')
this.setState({ visible: false })
}
render() {
const { visible } = this.state;
const { title, children } = this.props;
return <NewPortal visible={visible}>
<div className="modal-wrapper">
<div className="modal">
<div className="modal-title">{title}</div>
<div className="modal-content">{children}</div>
<div className="modal-operator">
<button
onClick={this.closeModal}
className="modal-operator-close"
>cancel</button>
<button
onClick={this.confirm}
className="modal-operator-confirm"
>confirm</button>
</div>
</div>
<div
className="mask"
onClick={this.maskClick}
></div>
</div>
</NewPortal>}}export default Modal;
Copy the code
Using the same OldPortal, let’s take a look in the browser to see if it works as we thought:
“Portals are the soul of a popover class of components. Their use here serves only as a guide to their core functionality, but does not go into the depth of some complex public approach. Interested readers can search for related articles that are explained in more detail.
2. Animation implementation of entry
1. Animation addition
Starting with a simple effect (using the code above for the Modal component using the NewPortal component), the Modal is gradually enlarged to 1.1 times when it pops up, then reduced to 1 times when it hides, first enlarged to 1.1 times, then reduced until it disappears.
The convention starts by thinking: What do we control to zoom in and out? How do we change the process of zooming in and out from an instant to a gradual process? When do we start zooming in and out? And when do you end up zooming in and out?
Zoom in and out we pass the CSS3 attribute transform We can use the scale parameter for each of the six states, and then we can use the transition to zoom in and out. We can use the scale parameter for each of the six states Excessive, should be able to achieve the effect we need:
Add the following code to modal.css:
.modal-enter {
transform: scale(0);
}
.modal-enter-active {
transform: scale(1.1);
transition: all .2s linear;
}
.modal-enter-end {
transform: scale(1);
transition: all .1s linear;
}
.modal-leave {
transform: scale(1);
}
.modal-leave-active {
transform: scale(1.1);
transition: all .1s linear;
}
.modal-leave-end {
transform: scale(0);
transition: all .2s linear;
}
Copy the code
The six class names define the six states that appear and disappear, and set the transition time of each. Then we can add the corresponding class name to the element in the different process, and can control the display state of the element.
Before we write the logic, we should also note that the implicit and explicit aspects of our components were actually controlled in the NewPortal component, but when we added the animation to the Modal component, we had to control the timing of the implicit and explicit aspects, such as starting the animation right after rendering and hiding after the animation, which would not be suitable for NewPortal The intermediate control is hidden. Some readers have wondered, why not just add animations to the NewPortal component? Of course the answer to this question is yes, but NewPortal’s function is delivery, not complex animation, and we want to keep it pure and not coupling with other components.
Modify newportal. JSX as follows:
import React from 'react';
import ReactDOM from 'react-dom';
class NewPortal extends React.Component {
constructor(props) {
super(props)
this.node = document.createElement('div');
document.body.appendChild(this.node);
}
render() {
const { children } = this.props;
return ReactDOM.createPortal(
children,
this.node, ); }}export default NewPortal
Copy the code
Modify modal.jsx as follows:
import React, { Component } from 'react';
import NewPortal from '.. /newPortal/newPortal';
import './modal.css';
class Modal extends Component {
constructor(props) {
super(props)
this.confirm = this.confirm.bind(this)
this.maskClick = this.maskClick.bind(this)
this.closeModal = this.closeModal.bind(this)
this.leaveAnimate = this.leaveAnimate.bind(this)
this.enterAnimate = this.enterAnimate.bind(this)
this.state = {
visible: false.classes: null,
}
}
componentDidMount() {
this.setState({ visible: this.props.visible })
}
componentWillReceiveProps(props) {
if (props.visible) {
// When the parent component props is received, render the animation if it is true
this.enterAnimate()
}
}
// Enter the animation
enterAnimate() {
// Here we define the class name for each state, which is the class we added earlier in the modal.css file
const enterClasses = 'modal-enter'
const enterActiveClasses = 'modal-enter-active'
const enterEndActiveClasses = 'modal-enter-end'
// This defines the transition time of each state, which corresponds to the transition time of the corresponding class name in modal. CSS, in milliseconds
const enterTimeout = 0
const enterActiveTimeout = 200
const enterEndTimeout = 100
// Change the explicit and implicit state to true and change classes to the name of the class in the Enter state
this.setState({ visible: true.classes: enterClasses })
// We use the timer because the function in the timer will be added to the event queue and will not be called until the main thread task has completed, which is equivalent to the enterTimeout time after the element is rendered and the initial class name is added.
// Since the start state doesn't need to be excessive, we'll just set it to 0.
const enterActiveTimer = setTimeout(_= > {
this.setState({ classes: enterActiveClasses })
clearTimeout(enterActiveTimer)
}, enterTimeout)
const enterEndTimer = setTimeout(_= > {
this.setState({ classes: enterEndActiveClasses })
clearTimeout(enterEndTimer)
}, enterTimeout + enterActiveTimeout)
// Finally, leave the class name blank to restore the element to its original state
const initTimer = setTimeout(_= > {
this.setState({ classes: ' ' })
clearTimeout(initTimer)
}, enterTimeout + enterActiveTimeout + enterEndTimeout)
}
// Leave the animation
leaveAnimate() {
const leaveClasses = 'modal-leave'
const leaveActiveClasses = 'modal-leave-active'
const leaveEndActiveClasses = 'modal-leave-end'
const leaveTimeout = 0
const leaveActiveTimeout = 100
const leaveEndTimeout = 200
// The initial element already exists, so there is no need to change the explicit or implicit state
this.setState({ classes: leaveClasses })
const leaveActiveTimer = setTimeout(_= > {
this.setState({ classes: leaveActiveClasses })
clearTimeout(leaveActiveTimer)
}, leaveTimeout)
const leaveEndTimer = setTimeout(_= > {
this.setState({ classes: leaveEndActiveClasses })
clearTimeout(leaveEndTimer)
}, leaveTimeout + leaveActiveTimeout)
// Finally change the explicit and implicit state to false and restore the class name to the initial state
const initTimer = setTimeout(_= > {
this.setState({ visible: false.classes: ' ' })
clearTimeout(initTimer)
}, leaveTimeout + leaveActiveTimeout + leaveEndTimeout)
}
closeModal() {
console.log('Hello, my name is Cancel, heard you want to order me? Tsundere face 👸 ')
const { onClose } = this.props
onClose && onClose()
// Click Cancel after calling leave animation
this.leaveAnimate()
}
confirm() {
console.log('Hello everyone, my name is Confirm, upstairs cancel is my son, a little brain ~')
const { confirm } = this.props
confirm && confirm()
this.leaveAnimate()
}
maskClick() {
console.log('Hello, I'm Mask, I've been clicked')
this.setState({ visible: false })
}
render() {
const { visible, classes } = this.state;
const { title, children } = this.props;
return <NewPortal>
<div className="modal-wrapper">
{
visible &&
<div className={`modalThe ${classes} `} >
<div className="modal-title">{title}</div>
<div className="modal-content">{children}</div>
<div className="modal-operator">
<button
onClick={this.closeModal}
className="modal-operator-close"
>cancel</button>
<button
onClick={this.confirm}
className="modal-operator-confirm"
>confirm</button>
</div>
</div>}} {/* * * * * * * * * * * * * * *<div
className="mask"
onClick={this.maskClick}
></div>* /}</div>
</NewPortal>}}export default Modal;
Copy the code
The effect is as follows:
2. Animation component encapsulation
The animation is implemented, but the code is all in modal. JSX, which is not elegant at all and is not reusable, so we need to consider abstracting it into a Transition component.
Idea: Let’s think about encapsulation in terms of the functionality points we need. First, the incoming explicit or implicit state value controls the explicit or implicit of the element. Given a class name that matches the corresponding six state class names; You can configure the transition time for each state; You can control whether animation is used or not;
Create a file transition.jsx in the SRC directory and create the following file:
import React from 'react';
// classnames are introduced to handle concatenation of classnames
import classnames from 'classnames';
class Transition extends React.Component {
constructor(props) {
super(props)
this.getClasses = this.getClasses.bind(this)
this.enterAnimate = this.enterAnimate.bind(this)
this.leaveAnimate = this.leaveAnimate.bind(this)
this.appearAnimate = this.appearAnimate.bind(this)
this.cloneChildren = this.cloneChildren.bind(this)
this.state = {
visible: false.classes: null,}}// The transition time is not passed in by default to 0
static defaultProps = {
animate: true.visible: false.transitionName: ' '.appearTimeout: 0.appearActiveTimeout: 0.appearEndTimeout: 0.enterTimeout: 0.enterActiveTimeout: 0.enterEndTimeout: 0.leaveTimeout: 0.leaveEndTimeout: 0.leaveActiveTimeout: 0,}// Here we add the first render animation. It only happens once
componentWillMount() {
const { transitionName, animate, visible } = this.props;
if(! animate) {this.setState({ visible })
return
}
this.appearAnimate(this.props, transitionName)
}
componentWillReceiveProps(props) {
const { transitionName, animate, visible } = props
if(! animate) {this.setState({ visible })
return
}
if(! props.visible) {this.leaveAnimate(props, transitionName)
} else {
this.enterAnimate(props, transitionName)
}
}
// First rendering of the entry animation
appearAnimate(props, transitionName) {
const { visible, appearTimeout, appearActiveTimeout, appearEndTimeout } = props
const { initClasses, activeClasses, endClasses } = this.getClasses('appear', transitionName)
this.setState({ visible, classes: initClasses })
setTimeout(_= > {
this.setState({ classes: activeClasses })
}, appearTimeout)
setTimeout(_= > {
this.setState({ classes: endClasses })
}, appearActiveTimeout + appearTimeout)
setTimeout(_= > {
this.setState({ classes: ' ' })
}, appearEndTimeout + appearActiveTimeout + appearTimeout)
}
// Entry animation
enterAnimate(props, transitionName) {
const { visible, enterTimeout, enterActiveTimeout, enterEndTimeout } = props
const { initClasses, activeClasses, endClasses } = this.getClasses('enter', transitionName)
this.setState({ visible, classes: initClasses })
const enterTimer = setTimeout(_= > {
this.setState({ classes: activeClasses })
clearTimeout(enterTimer)
}, enterTimeout)
const enterActiveTimer = setTimeout(_= > {
this.setState({ classes: endClasses })
clearTimeout(enterActiveTimer)
}, enterActiveTimeout + enterTimeout)
const enterEndTimer = setTimeout(_= > {
this.setState({ classes: ' ' })
clearTimeout(enterEndTimer)
}, enterEndTimeout + enterActiveTimeout + enterTimeout)
}
// Exit animation
leaveAnimate(props, transitionName) {
const { visible, leaveTimeout, leaveActiveTimeout, leaveEndTimeout } = props
const { initClasses, activeClasses, endClasses } = this.getClasses('leave', transitionName)
this.setState({ classes: initClasses })
const leaveTimer = setTimeout(_= > {
this.setState({ classes: activeClasses })
clearTimeout(leaveTimer)
}, leaveTimeout)
const leaveActiveTimer = setTimeout(_= > {
this.setState({ classes: endClasses })
clearTimeout(leaveActiveTimer)
}, leaveActiveTimeout + leaveTimeout)
const leaveEndTimer = setTimeout(_= > {
this.setState({ visible, classes: ' ' })
clearTimeout(leaveEndTimer)
}, leaveEndTimeout + leaveActiveTimeout + leaveTimeout)
}
// Unified class name configuration
getClasses(type, transitionName) {
const initClasses = classnames({
[`${transitionName}-appear`]: type === 'appear'[`${transitionName}-enter`]: type === 'enter'[`${transitionName}-leave`]: type === 'leave',})const activeClasses = classnames({
[`${transitionName}-appear-active`]: type === 'appear'[`${transitionName}-enter-active`]: type === 'enter'[`${transitionName}-leave-active`]: type === 'leave',})const endClasses = classnames({
[`${transitionName}-appear-end`]: type === 'appear'[`${transitionName}-enter-end`]: type === 'enter'[`${transitionName}-leave-end`]: type === 'leave',})return { initClasses, activeClasses, endClasses }
}
cloneChildren() {
const { classes } = this.state
const children = this.props.children
const className = children.props.className
// Add extra props to child elements via React. CloneElement,
return React.cloneElement(
children,
{ className: `${className} ${classes}` }
)
}
render() {
const { visible } = this.state
return visible && this.cloneChildren()
}
}
export default Transition
Copy the code
Modal.jsx contents are modified as follows:
import React, { Component } from 'react';
import NewPortal from '.. /newPortal/newPortal';
import Transition from '.. /transition/transition';
import './modal.css';
class Modal extends Component {
constructor(props) {
super(props)
this.confirm = this.confirm.bind(this)
this.maskClick = this.maskClick.bind(this)
this.closeModal = this.closeModal.bind(this)
this.state = {
visible: false,
}
}
componentDidMount() {
this.setState({ visible: this.props.visible })
}
componentWillReceiveProps(props) {
this.setState({ visible: props.visible })
}
closeModal() {
console.log('Hello, my name is Cancel, heard you want to order me? Tsundere face 👸 ')
const { onClose } = this.props
onClose && onClose()
this.setState({ visible: false })
}
confirm() {
console.log('Hello everyone, my name is Confirm, upstairs cancel is my son, a little brain ~')
const { confirm } = this.props
confirm && confirm()
this.setState({ visible: false })
}
maskClick() {
console.log('Hello, I'm Mask, I've been clicked')
this.setState({ visible: false })
}
render() {
const { visible } = this.state;
const { title, children } = this.props;
return <NewPortal>{/* Introduce the transition component, removing the outer modal wrapper */}<Transition
visible={visible}
transitionName="modal"
enterActiveTimeout={200}
enterEndTimeout={100}
leaveActiveTimeout={100}
leaveEndTimeout={200}
>
<div className="modal">
<div className="modal-title">{title}</div>
<div className="modal-content">{children}</div>
<div className="modal-operator">
<button
onClick={this.closeModal}
className="modal-operator-close"
>cancel</button>
<button
onClick={this.confirm}
className="modal-operator-confirm"
>confirm</button>
</div>
</div>{/* this mask can also be wrapped with the transition component, adding a fade in and out effect, but not here, you can try it yourself */} {/*<div
className="mask"
onClick={this.maskClick}
></div>* /}</Transition>
</NewPortal>}}export default Modal;
Copy the code
This is the end of the article. For the sake of reading integrity, each step is the complete code posted, resulting in a long text. Thank you for your reading.
This article code address, welcome star~