Skip to content

自定义组件案例:弹框组件

在使用 RN 进行跨平台应用开发时,官方提供的组件往往是有限的,并且很多组件并不是多平台通用的,有些只针对特别的平台。此时,要想在应用开发上保持页面样式的一致性,除了直接选择第三方开源库以外,另一个有效的手段就是自定义组件。

本小节我们来自定义一个弹框组件。

首先我们简单复习一下 ES6 中导入导出模块的相关知识。

// 组件导出
export default class App extends Component{
...
}
// 组件导入
import App from './App'

除了组件外,变量和常量也支持导入和导出。

// 变量和常量导出
var name = '张三'
const age = '18'
export { name, age }
// 变量和常量的导入
import { name, age } from './App'

方法的导入以及导出,和变量、常量的导入导出类似。

// 方法导出
export function sum(a, b) {
return a + b
}
// 方法导入
import { sum } from './Util'

另外,我们还会使用到 propTypes。通常,通用组件需要使用自定义属性的方式接收外界传入的值,如果是必须要传入的值,可以使用 isRequired 关键字。例如:

static propTypes = {
title: PropTypes.string.PropTypes.func.isRequired,
content: PropTypes.string,
}

需要注意的是,由于 propTypes15.0.0 版本中已经被移除掉了,所以在 15.5.0 以及之后的版本中,需要使用新的方式引入。

import PropTypes from 'prop-types'

好了,前置内容介绍完毕后,接下来我们就来封装第一个自定义组件——弹框组件。

image-20220620132110129

上图是封装好之后的效果,可以看到,整个弹框由 4 个部分组成,分别是图片、标题、内容、确认按钮以及关闭按钮。其中图片、标题、内容、确认按钮内容都是应该在使用组件时传递进去的。

完整的封装组件代码如下:

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import {
View,
Text,
TouchableOpacity,
Image,
StyleSheet,
ImageBackground,
Dimensions,
} from 'react-native'
const { width } = Dimensions.get('window')
export default class FreeDialog extends Component {
static propTypes = {
isShow: PropTypes.bool.isRequired,
title: PropTypes.string,
content: PropTypes.string,
buttonContent: PropTypes.string,
closeDialog: PropTypes.func.isRequired,
imageSource: PropTypes.string.isRequired,
}
closeDialogHandle() {
this.props.closeDialog()
}
render() {
if (!this.props.isShow) {
return null
} else {
return (
<View style={styles.containerBg}>
<View style={[styles.dialogBg]}>
<Image source={this.props.imageSource} style={styles.logoStyle} />
<Text style={styles.titleStyle}>{this.props.title}</Text>
<Text style={styles.contentStyle}>{this.props.content}</Text>
<TouchableOpacity>
<ImageBackground
resizeMode="stretch"
source={require('../images/commen_btn.png')}
style={styles.buttonStyle}
>
<Text style={styles.btnContentStyle}>
{this.props.buttonContent}
</Text>
</ImageBackground>
</TouchableOpacity>
</View>
<TouchableOpacity
style={styles.btnCloseStyle}
onPress={this.closeDialogHandle.bind(this)}
>
<Image
source={require('../images/ic_close.png')}
style={{ height: 38, width: 38 }}
/>
</TouchableOpacity>
</View>
)
}
}
}
const styles = StyleSheet.create({
containerBg: {
backgroundColor: 'rgba(0, 0, 0, 0.7)',
position: 'absolute',
left: 0,
right: 0,
bottom: 0,
top: 0,
overflow: 'hidden',
justifyContent: 'center',
alignItems: 'center',
},
dialogBg: {
width: width - 100,
backgroundColor: '#ffffff',
borderRadius: 10,
overflow: 'hidden',
alignItems: 'center',
},
logoStyle: {
height: ((width - 100) * 258) / 400,
width: width - 100,
},
titleStyle: {
marginTop: 14,
color: '#333333',
fontSize: 18,
fontWeight: '600',
},
contentStyle: {
marginTop: 5,
color: '#333333',
fontSize: 14,
fontWeight: '400',
},
buttonStyle: {
height: ((width - 135) * 88) / 480,
width: width - 180,
marginTop: 36,
marginBottom: 22,
alignItems: 'center',
justifyContent: 'center',
},
btnContentStyle: {
fontSize: 16,
color: 'white',
textAlign: 'center',
fontWeight: '600',
},
btnCloseStyle: {
padding: 10,
marginTop: 33,
alignItems: 'center',
},
})

App.js 根组件中使用测试:

import React, { PureComponent } from 'react'
import {
TouchableOpacity,
StyleSheet,
Text,
View,
Dimensions,
} from 'react-native'
import FreeDialog from './components/FreeDialog'
const { width } = Dimensions.get('window')
export default class DialogPage extends PureComponent {
constructor(props) {
super(props)
this.state = {
isShowDialog: false,
}
}
renderDialog() {
return (
<FreeDialog
isShow={this.state.isShowDialog}
closeDialog={this.closeDialog.bind(this)}
title={'年终大促'}
content={'您有新的新年礼品,请查收!'}
buttonContent={'新年礼品请查收'}
imageSource={require('./images/dialog_bg.png')}
/>
)
}
showDialog() {
this.setState({
isShowDialog: true,
})
}
closeDialog() {
this.setState({
isShowDialog: false,
})
}
render() {
return (
<View style={styles.container}>
<TouchableOpacity
style={styles.btnContainer}
onPress={this.showDialog.bind(this)}
>
<Text style={styles.textStyle}>免费咨询医生</Text>
</TouchableOpacity>
{this.renderDialog()}
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
btnContainer: {
marginTop: 15,
marginLeft: 10,
marginRight: 10,
backgroundColor: '#EE7942',
height: 38,
width: width - 100,
borderRadius: 5,
justifyContent: 'center',
alignItems: 'center',
},
textStyle: {
color: '#ffffff',
fontSize: 18,
},
})