rn-relates
rn-relates copied to clipboard
组件封装
props & state
在进行组件封装之前,需要先对 props 和 state 有所了解,这里是官方文档说明。简单地讲,props 是作为参数传递到子组件的,子组件通过接收不同的 props ,结合组件已有的通用功能或是样式,达到组件复用效果。当组件内部需要更新某些状态的时候,这时候则需要使用到 state 。
无状态组件(Stateless Functional Component)
在大部分情况下,封装的组件都不需要自己维护内部状态,只当做 UI 展示组件,由外部传入的 props 来决定不同的渲染效果。在这种情况下,我们应当优先选择无状态组件,这里只用 ES6 语法作为示例:
/*
* StaticCell.js
* Stateless Functional Component
*/
// import ...
const StaticCell = ({ title, icon }) => {
return (
<View style={styles.cell}>
<Text style={styles.title}>{title}</Text>
<Image source={icon} style={styles.icon}/>
</View>
)
}
StaticCell.propTypes = {
title: React.PropTypes.string,
icon: React.PropTypes.any
};
StaticCell.defaultProps = {
title: '个人信息',
icon: require('./img/arrow.png')
};
export default StaticCell;
在封装无状态组件时,建议配合 ES6 的解构、箭头函数语法,无论从编写到阅读,这种方式都比较简洁。不过无状态组件不支持ref引用,没有生命周期方法和内部状态管理,在组件渲染的时候直接挂载,省略了一些生命周期方法中的检查处理,也没有了新建类实例操作,所以开销是比较小的。该方式应当做组件封装的优先方式。
类组件(Class Component)
当然,并不是所有的组件都是无状态组件,比如计时器组件。该例子主要基于自己封装的组件 rn-coundown,在一个获取验证码的计时器组件中,我们可以根据其行为表现来判断 props 和 state 的组成:
- 计时状态:一个计时器组件,其状态应包括初始、计时中和计时结束三种状态。当外界改变组件初始状态后,计时中、计时结束都应由组件自己维护,因此在
state中设置status变量,赋值范围为Idle、Counting和Over,分别代表不同状态- 秒数:毫无疑问需要在
state中保存秒数变量,这里命名second
state 中保存以上变量即可。标题、计时中标题、计时结束标题,这三个文案实际上基于计时器状态,所以当外界通过 props 提供相应值后,组件内部通过自己的 status 来设置不同文案即可。在计时的不同状态中,文案样式可能不同,所以这些设置都可以列入 props 里面,由外界提供。示例代码主要演示不同状态下的标题设置:
render() {
const { status, second } = this.state;
const { title, countingTitleTemplate, overTitle } = this.props;
// 初始标题,如:获取验证码
let promptTitle = title;
if (status === CountdownStatus.Counting) {
// 计时中标题,如:60s后重新获取
promptTitle = countingTitleTemplate.replace('{time}', second);
} else if (status === CountdownStatus.Over) {
// 计时结束标题,如:重新获取
promptTitle = overTitle;
}
return <View>...</View>
}
在发起一个验证码请求之前,一般都会做一些逻辑处理,比如是否输入手机号码,或者手机号码是否合法,在未满足某些业务逻辑之前,组件是不应开始计时操作的。那么,如何来捕捉组件外这些行为呢?
在当前组件的最新版本中,提供了 shouldStartCountdown 函数作为参数,该函数会返回一个 bool 值,当点击组件时,组件内部会针对该返回值来控制计时器的开启时机:
handlePress = () => {
if (this.isNetworkFailed() || !this.canStartTimer()) return;
this.setState({status: CountdownStatus.Counting}, this.startTimer);
this.shouldShowWarningInfo();
};
isNetworkFailed = () => {
const {onNetworkFailed} = this.props;
const {isConnected} = this.state;
// network is failed
if (!isConnected) {
onNetworkFailed && onNetworkFailed();
}
return !isConnected;
};
canStartTimer = () => {
const {shouldHandleBeforeCountdown, shouldStartCountdown} = this.props;
let canStartTimer = shouldStartCountdown();
if (shouldHandleBeforeCountdown !== undefined && typeof shouldHandleBeforeCountdown === 'function') {
canStartTimer = shouldHandleBeforeCountdown();
console.warn(`[rn-countdown] Warning: "shouldHandleBeforeCountdown" is deprecated, use "shouldStartCountdown" instead.`);
}
return canStartTimer;
};
以上部分代码对老版本组件的参数做了兼容处理,同时也给出了相应提示,以方便开发人员跟进这些变更。因同事使用反馈,最新版本中也添加了对网络状态的处理,当网络连接失败时,则提供 onNetworkFailed 回调,开发人员可在该回调中做一些提示处理,并且倒计时不会开始。
外部使用:
shouldStartCountdown = () => {
// 可以开始计时
if (this.phoneNumber) return true;
// 不满足业务逻辑,返回false表示不能开始计时
alert('电话号码不能为空!');
return false;
};
除此之外,当计时器运行过程中遇到网络请求失败,或是其他情况,需要手动停止计时器,显示计时结束文案,那么基于这种需求,可以向外提供一个 stopCountdown 方法,然后外界通过 ref 引用来结束计时器。
总结:
- 封装一个类组件,记住大部分的变量都可以作为
props,只有影响内部状态的,才需要放在state- 在实际项目中去使用组件,然后借助业务逻辑,不断完善组件功能
- 按需在
shouldComponentUpdate(nextProps, nextState)方法中做优化处理- 组件的参数为简单数据类型时,使用
PureComponent
附类组件基本代码规范:
export default class MyComponent extends Component {
static displayName = 'MyComponent';
static propTypes = {};
static defaultProps = {};
constructor(props) {
super(props);
this.state = {}
}
// React 16.3 将移除该生命周期方法
componentWillMount() {}
componentDidMount() {}
// React 16.3 将移除该生命周期方法
componentWillReceiveProps(nextProps) {}
shouldComponentUpdate(nextProps, nextState) {}
componentWillUnmount() {}
render() {}
}
最新版本:
- 已经移除旧的
shouldHandleBeforeCountdown,不再对该props做判断和信息提示 startCountdown中不再重复shouldStartCountdown的逻辑,只关注网络状态