组件介绍
React中组件主要可分为函数组件和类组件,两者区别是函数组件没有state和生命周期,故函数组件也称为 stateless functional components, 适用于仅进行简单渲染操作的组件。
此外,还有高阶组件HOC(higher-order component), 即高阶函数,接收一个React组件作为参数,然后返回另外的React组件,通常是原组件的加强版。
函数组件
可接收props,返回一个JSX元素,例如
function ReactHeader(props) {
return <h1>React {props.version} Documentation</h1>
}
<ReactHeader version={18}></ReactHeader>
经过Babel转义后为
function ReactHeader(props) {
return React.createElement(
"h1",
null,
"React ",
props.version,
" Documentation"
);
}
React.createElement(ReactHeader, { version: 18 });
函数组件参数初始化
- 在函数组件上添加一个名为defaultProps的静态属性
function ThemedButton(props) {
const { theme, label, ...restProps } = props;
return <button className={`btn btn-${theme}`} {...restProps}>{ label }</button>
}
// 设置默认属性
ThemedButton.defaultProps = {
theme: "secondary",
label: "Button Text"
};
- 使用ES6的对象解构语法
// METHOD 1:
function ThemedButton(props) {
const { theme = 'secondary', label = 'Button Text', ...restProps } = props;
return <button className={`btn btn-${theme}`} {...restProps}>{ label }</button>
}
// METHOD 2:
// 更加紧凑的方式
function ThemedButton({ theme = 'secondary', label = 'Button Text', ...restProps }) {
return <button className={`btn btn-${theme}`} {...restProps}>{ label }</button>
}
类组件
可以使用ES6的extends写法,15.4之前可利用React.creatClass创建类组件,但如今已经弃用了。
关于两者区别可参考这里
考虑到props是不建议修改的,React推荐数据源从上往下流,仅有一个被trusted的数据源,其余由dispatch发送事件,subscribe订阅事件, store存储所有信息,该思想为flux。为了追踪一些状态或变量,React在class中提供了state对象,用于存储组件私有变量,对state的改变则通过setState进行
示例:
class simple extends React.Component {
constructor(){
super(props)
this.state = {
status: ''
}
}
render() {
return (
<h1>Simple test</h1>
)
}
}
console.log(<simple />);
经过Babel转义后
var simple = function (_React$Component) {
_inherits(simple, _React$Component);
function simple() {
_classCallCheck(this, simple);
var _this = _possibleConstructorReturn(this, (simple.__proto__ || Object.getPrototypeOf(simple)).call(this, props));
_this.state = {
status: ''
};
return _this;
}
_createClass(simple, [{
key: 'render',
value: function render() {
return React.createElement(
'h1',
null,
'Simple test'
);
}
}]);
return simple;
}(React.Component);
console.log(React.createElement('simple', null));
类组件初始化
类组件的初始化也可在组件上添加一个名为defaultProps的静态属性, 定义一个名为Component的组件后
Component.defaultProps = {
name: "admin",
age: 20
};
或者
//在组件内设置(与constructor平级)
static defaultProps = {
name: 'com',
age: 18
}
组件参数类型检测
组件的类型检测需要额外引入prop-types
<script src="./prop-types.js"></script>
然后在组件上应用propTypes
Component.propTypes = {
name: PropTypes.string,
age: PropTypes.number
}
关于类型检测更多种类可参考这里
组件this绑定
与ES6中类语言一致,通过extends得到的class,组件内的方法将不会自动将this绑定到实例上,有三种方式可以进行绑定
实例:
constructor(props){
super(props)
this.state = {
status: 'logged',
message: ''
}
this.handleChange = this.handleChange.bind(this)
}
handleChange(e){
console.log('handle Change')
console.log(this); // 在constructor中通过bind绑定
this.setState({message:e.target.value})
console.log(this.state.message)
}
handleFocus(){
console.log('handle Focus')
console.log(this); // 在render的时候通过bind绑定
}
handleBlur = ()=>{
console.log('handle Blur')
console.log(this); // 用箭头函数自动绑定
}
render(){
console.log(this.props)
return (
//写style是需要加双括号,JSX编码格式要求,style必须是一个对象
<div style={{color:"blue"}}>我是Component组件 >> {undefined}
<p>{this.props.children}</p>
<p>Name: {this.props.name}</p>
<p>Age: {this.props.age}</p>
<p>Status: {this.state.status}</p>
<p>Message: {this.state.message}</p>
<input type="text" value={this.state.message} onChange={this.handleChange}
onFocus={this.handleFocus.bind(this)}
//第一、三种写法的性能一样,在实例化时会调用
//但万一我们需要改变语法结构,第一种方式可以完全不需要去改动 JSX 的部分:
//第二种则会在触发render时不断进行调用,性能不太好
onBlur={this.handleBlur}/>
</div>
)
}
另外可通过以下方式,将函数组件转为类组件
a. 通过class 声明与函数同名的类,该类继承React.Component
b. 添加一个空方法,名为render
c. 将函数主体移入render方法中
d. 将render中的props替换为this.props
e. 将props中需要修改的data移入state中
f. 在类的render中,将this.props.data替换为this.state.data
g. 创建一个class constructor 用于初始化this.state
类组件生命周期
React类组件生命周期有较大的变动,我写这篇文章的时候是V16.7,V16.3开始官方已经申明以下生命周期函数会在下一个大版本停用,为了兼容性,在这些过时的生命周期函数前加前缀UNSAFE__
- componentWillMount
- componentWillReceiveProps
- componentWillUpdate
取而代之的 - static getDerivedStateFromProps
取代componentWillReceiveProps
特点是纯函数,且由于是静态方法故无法获取this - getSnapshotBeforeUpdate
可取代componentWillUpdate
官方提出改动一是WillMount争议较大,可参见这里存在滥用情况多,去除后两个是为了以后增加异步渲染的过程。
有一种错觉,在componentWillMount请求的数据在render就能拿到,但其实render在willMount之后几乎是马上就被调用,根本等不到数据回来,同样需要render一次“加载中”的空数据状态,所以在didMount去取数据几乎不会产生影响。另外,react只能保证componentDidMount-componentWillUnmount成对出现,componentWillMount可以被打断或调用多次,因此无法保证事件监听能在unmount的时候被成功卸载,可能会引起内存泄露
以下是新旧生命周期示意图对比
[图片上传中...(React_lifecycle_old.png-b3c3f5-1546961611183-0)]
[图片上传中...(React_lifecycle_new.png-68ca8-1546961671751-0)]
建议用法:
- constructor初始化state
- componentDidMount
此过程请求异步加载的数据
并添加事件监听 - getDerivedStateFromProps(nextProps,preState)
根据props更新之前的state,若需要在Setstate前进行对比props是否有更新,需在shouldComponentUpdate中使用
if (nextProps.currentRow !== prevState.lastRow) {
return {
...
lastRow: nextProps.currentRow,
};
// 不更新state
return null
}
componentDidUpdate
处理由state或props更新触发的请求getDerivedStateFromProps
传入新的props重新异步读取数据
static getDerivedStateFromProps(nextProps, prevState) {
// Store prevId in state so we can compare when props change.
if (nextProps.id !== prevState.prevId) {
return {
externalData: null,
prevId: nextProps.id,
};
}
// No state update necessary
return null;
}
componentDidUpdate(prevProps, prevState) {
if (this.state.externalData === null) {
this._loadAsyncData(this.props.id);
}
}
参考部分1
[参考部分2]