手把手教你封裝一個Modal元件

薔薇愛學習發表於2018-10-02

環境搭建

create-react-app 快速搭建一個 react 的開發環境,沒有用過的童鞋可參考官網

目錄

  1. 新建檔案 src/modal/index.jsx, 並寫入一段簡單的測試程式碼
import React, { Component } from 'react';
import './index.css';
class Modal extends Component {
  render() {
    return <div className="modal">
      這是一個modal元件
    </div>
  }
}
export default Modal;
複製程式碼
  1. 新建檔案 src/modal/index.css

  2. 修改 src/App.js, 引入 Modal 元件

import React, { Component } from 'react';
import Modal from './modal';
import './App.css';

class App extends Component {
  render() {
    return (
      <Modal / >
    );
  }
}

export default App;
複製程式碼
  1. 在命令列輸入npm start,出現如下結果,則表示環境搭建成功

modal_init.png

什麼是 modal

  • 標題區
  • 內容區
  • 控制區
  • mask

modal 骨架實現

修改src/modal/index.jsx

import React from 'react';

export default class Modal extends React.Component {
	render() {
		return (
			<div>
				<div>
					<div>標題區</div>
					<div>內容區</div>
					<div>
						<button>取消</button>
						<button>確定</button>
					</div>
				</div>
				<div>mask</div>
			</div>
			)
	}
}
複製程式碼

modal 樣式實現

修改src/modal/index.jsx

import React from 'react';

export default class Modal extends React.Component {
	render() {
		return (
			<div className='wrapper'>
				<div className='modal'>
					<div className='title'>標題區</div>
					<div className='content'>內容區</div>
					<div className='operator'>
						<button className='close'>取消</button>
						<button className='confirm'>確定</button>
					</div>
				</div>
				<div className='mask'>mask</div>
			</div>
			)
	}
}
複製程式碼

修改 src/modal/index.css

.modal {
	width: 300px;
	height: 200px;
	position: fixed;
	top: 0;
	bottom: 0;
	left: 0;
	right: 0;
	margin: auto;
	z-index: 2000;
	background: #fff;
	border-radius: 2px;
	box-shadow:  inset 0 0 1px 0 #000;
}

.title {
	width: 100%;
	height: 50px;
	line-height: 50px;
	padding: 0 10px;
}

.content {
	width: 100%;
	height: 100px;
	padding: 0 10px;
}

.operator {
	width: 100%;
	height: 50px;

}

.close, .confirm {
	width: 50%;
	border: none;
	outline: none;
	color: #fff;
	background: #4CA791;
	cursor: pointer;
	
}
.close:active, .confirm:active {
	opacity: 0.6;
	transition: opacity 0.3s;
}

.mask {
	position: fixed;
	top: 0;
	left: 0;
	right: 0;
	bottom: 0;
	background: #000;
	opacity: 0.6;
	z-index: 1000;
}
複製程式碼

效果如圖所示:

modal_css.png

modal 功能開發

先思考一下 modal 元件需要實現哪些基本功能:

  • 可以通過 visible 控制 modal 的顯隱
  • 標題區 和 內容區 可以自定義顯示內容
  • 點選取消關閉 modal, 同時會呼叫名為 onClose 的回撥
  • 點選確認會呼叫名為 onConfirm 的回撥,並關閉 modal
  • 點選蒙層 mask 關閉 modal
  • animate 欄位可以開啟/關閉動畫

控制 modal 顯隱

修改 src/modal/index.jsx

import React from 'react';

import './index.css';

export default class Modal extends React.Component {
	constructor(props) {
		super(props)
	}
	render() {
		// 通過父元件傳遞的 visible 控制顯隱
		const { visible = true } = this.props
		return visible &&
		(
			<div className='wrapper'>
				<div className='modal'>
					<div className='title'>標題區</div>
					<div className='content'>內容區</div>
					<div className='operator'>
						<button className='close'>取消</button>
						<button className='confirm'>確定</button>
					</div>
				</div>
				<div className='mask'>mask</div>
			</div>
		)
	}
}
複製程式碼

修改 src/App.js, 通過一個 Button 來控制 modal 的顯隱

import React, { Component, Fragment } from 'react';
import Modal from './modal';
import './App.css';


class App extends Component {
	constructor(props) {
		super(props)
		this.state = {
			visible: false,
		}
	}
	showModal = () => {
			this.setState({
				visible: true,
			});
	}
  render() {
  	const { visible } = this.state
    return (
    	<Fragment>
	    	<Modal visible={visible}/ >
	    	<button onClick={() => this.showModal()} style={{
	    		'background': '#4CA791',
	    		'color': '#fff',
	    		'border': 'none',
	    		'width': 300,
	    		'height': 50,
	    		'fontSize': 30,
	    	}}>切換顯隱</button>
    	</Fragment>
      
    );
  }
}

export default App;

複製程式碼

標題區和內容區可自定義

修改 src/modal/index.jsx

import React from 'react';

import './index.css';

export default class Modal extends React.Component {
	constructor(props) {
		super(props)
	}
	render() {
		// 通過父元件傳遞的 visible 控制顯隱
		const { visible = true, title, children } = this.props
		return visible &&
		(
			<div className='wrapper'>
				<div className='modal'>
					<div className='title'>{title}</div>
					<div className='content'>{children}</div>
					<div className='operator'>
						<button className='close'>取消</button>
						<button className='confirm'>確定</button>
					</div>
				</div>
				<div className='mask'>mask</div>
			</div>
		)
	}
}
複製程式碼

修改 src/App.js, 從外部傳入自定義的 ‘title' 和 ‘content'

import React, { Component, Fragment } from 'react';
import Modal from './modal';
import './App.css';

class App extends Component {
	constructor(props) {
		super(props)
		this.state = {
			visible: false,
		}
	}
	showModal = () => {
			this.setState({
				visible: true,
			});
	}
  render() {
  	const { visible } = this.state
    return (
    	<Fragment>
	    	<Modal
	    	visible={visible}
	    	title='這是自定義的title'>
	    	這是自定義的content
	    	</Modal>
	    	<button onClick={() => this.showModal()} style={{
	    		'background': '#4CA791',
	    		'color': '#fff',
	    		'border': 'none',
	    		'width': 300,
	    		'height': 50,
	    		'fontSize': 30,
	    	}}>切換顯隱</button>
    	</Fragment>
      
    );
  }
}

export default App;

複製程式碼

控制區功能及蒙層點選功能

  • 要實現點選取消按鈕關閉 modal, 那麼就需要在 modal 中維護一個狀態,然後用這個狀態來控制 modal 的顯隱,好像可行
  • 但是前面我們是通過父元件的 visible 控制 modal 的顯隱,似乎矛盾
  • 要結合起來,只通過 state 來控制 modal 的顯隱,props 改變只會改變 state

修改 src/modal/index.jsx

import React from 'react';

import './index.css';

export default class Modal extends React.Component {
	constructor(props) {
		super(props)
		this.state = {
			visible: false
		}
	}
	
	// 首次渲染使用父元件的狀態更新 modal 中的 visible 狀態,只呼叫一次
	componentDidMount() {
		this.setState({
			visible: this.props.visible
		})
	}

	// 每次接收 props 就根據父元件的狀態更新 modal 中的 visible 狀態,首次渲染不會呼叫
	componentWillReceiveProps(props) {
		this.setState({
			visible: props.visible
		})
	}

	handleClose = () => {
		const { onClose } = this.props
		onClose && onClose()
		this.setState({
			visible: false
		})
	}
	handleConfirm = () => {
		const { onConfirm } = this.props
		onConfirm && onConfirm()
		this.setState({
			visible: false
		})
	}
	handleMask = () => {
		this.setState({
			visible: false
		})
	}
	render() {
		// 通過父元件傳遞的 visible 控制顯隱
		const { title, children } = this.props

		const { visible = true } = this.state

		return visible &&
		(
			<div className='wrapper'>
				<div className='modal'>
					<div className='title'>{title}</div>
					<div className='content'>{children}</div>
					<div className='operator'>
						<button className='close' onClick={this.handleClose}>取消</button>
						<button className='confirm' onClick={this.handleConfirm}>確定</button>
					</div>
				</div>
				<div className='mask' onClick={this.handleMask}>mask</div>
			</div>
		)
	}
}
複製程式碼

修改 src/App.js, 從外部傳入自定義的 onCloseonConfirm

import React, { Component, Fragment } from 'react';
import Modal from './modal';
import './App.css';



class App extends Component {
	constructor(props) {
		super(props)
		this.state = {
			visible: false,
		}
	}
	showModal = () => {
			this.setState({
				visible: true,
			});
	}
	onClose = () => {
		console.log('onClose');
	}
	onConfirm = () => {
		console.log('onConfirm');
	}
  render() {
  	const { visible } = this.state
    return (
    	<Fragment>
	    	<Modal
	    	visible={visible}
	    	title='這是自定義的title'
	    	onClose={this.onClose}
	    	onConfirm={this.onConfirm}
	    	>
	    	這是自定義的content
	    	</Modal>
	    	<button onClick={() => this.showModal()} style={{
	    		'background': '#4CA791',
	    		'color': '#fff',
	    		'border': 'none',
	    		'width': 300,
	    		'height': 50,
	    		'fontSize': 30,
	    	}}>切換顯隱</button>
    	</Fragment>
      
    );
  }
}

export default App;

複製程式碼

React技術交流群.jpeg

相關文章