组件
1.函数式组件
-
什么是函数式组件
创建一个函数,只要函数中返回一个新的JSX元素,则为函数式组件
import React from 'react'
function News(props){ // 声明函数式组件
console.log(props); //接收只读属性
return <div>
<ul>
<li>webpack</li>
<li>vue</li>
<li>react</li>
</ul>
</div>
}
export default News;
-
调用组件
可以是单闭合,也可以是双闭合。双闭合方式可以把一些子节点当作属性(children)传递给组件,在组件中可以把传递的这些节点放在指定的位置
// index.js
ReactDOM.render(<>
<Dialog con='嘿嘿嘿' />
<Dialog con='呵呵呵' lx={1} >
<span>1</span>
<span>2</span>
</Dialog>
</>, document.getElementById('root'));
// Dialog.js
export default function Dialog(props) {
let {con, lx = 0, children, style = {}} = props,
title = lx === 0 ? '系统提示' : '系统警告';
return <section style={style}>
<h2>{title}</h2>
<div>{con}</div>
{/*把属性中传递的子元素放到组件中的指定位置*/}
{ children }
{/*也可以基于REACT中提供的专门遍历CHILDREN的方法来完成遍历操作*/}
{
React.Children.map(children, item => item)
}
</section>;
};
-
静态组件
每次调用函数组件,都会重新进行渲染和计算,把渲染后的结果呈现在页面中,渲染完成后呈现的内容将不再改变,除非重新调用该组件
2.类组件
-
什么是类组件
创建一个类,让其继承React.Component
或者React.PureComponent
,此类被称为类组件 -
基于状态管理动态组件:
1.设置初始状态值
2.修改状态:setState
修改组件中的状态
import React from 'react'
export default class Clock extends React.Component{
// 调取组件,创建类的一个实例,首先执行constructor,把属性、上下文等信息传递进来
constructor(props){
super(props);
// 如果只写SUPER():虽然创建实例的时候把属性传递进来了,但是并没有传递父组件,也就是没有把属性挂载到实例上,使用THIS.PROPS获取的结果是UNDEFINED
// 如果SUPER(PROPS):在继承父类私有的时候,就把传递的属性挂载到了子类的实例上,CONSTRUCTOR中就可以使用THIS.PROPS了
console.log(this.props); // 接收的只读属性
// 创建初始状态
this.state = {
time: new Date().toLocaleString()
}
}
// render渲染组件的内容
render(){
return <div>
{this.state.time}
</div>
}
// componentDidMount:生命周期 第一次渲染完
componentDidMount(){
setInterval(() => {
// 修改状态,并且通知组件重新渲染
this.setState({
time: new Date().toLocaleString()
});
}, 1000);
}
-
属性的操作
利用第三方插件prop-types
可以设置属性的规则
// test.jsx
import PropTypes from 'prop-types';
...
// 设置默认属性
static defaultProps = {
m: 100
};
// 属性验证
static propTypes = {
m: PropTypes.number,
x: PropTypes.string.isRequired
};
...
// index.js
...
<Clock m={1} x='pass'/>
...
-
非受控组件
不受状态管控的组件(通过ref方式),有时我们会需要直接对某个DOM节点或组件进行操作,而不是通过状态,此时会运用到非受控组件,对应的概念:受控组件:受状态管控的组件 => 数据驱动视图渲染
// 以下是三种使用ref的方法
export default class Input extends React.Component {
constructor(){
super();
this.refObj = React.createRef(); // { current: null } method 3
}
render() {
return <div>
<input type="text" ref='inpBox' /> // method 1,不推荐使用
<input type="text" ref={element => { // method 2
// element当前的元素对象
this.inp = element;
}} />
<input type="text" ref={ this.refObj } /> // method3
</div>;
}
componentDidMount() {
this.refs.inpBox.focus(); // method 1,不推荐使用
this.inp.focus(); // method 2
this.refObj.current.focus(); // method3
}
}
3.细节知识点
- REACT中的事件是合成事件,即所有的事件都是进行事件代理的,而且事件对象也是合成的,故会出现以下情况。解决这一问题的方法有两种,一是采用bind改变this指向,二是采用ES6的箭头函数
render(){
return <div>
<button onClick={this.handle}>button</button>
</div>
}
handle(ev){
console.log(this); // undefined
console.log(ev); // 事件对象
}
<button onClick={this.handle.bind(this)}>button</button> //method 1
<button onClick={()=>console.log(this)}>button</button> //method 2
- 当使用
setState
进行状态设置时,即使状态不发生变化,仍然会触发render的重新渲染,此时应当考虑对其进行优化,可以在shouldComponentUpdate
中进行手动对比设置,也可以直接让类组件继承React.PureComponent
来自动进行浅对比(引用变化是检测不出来的)
handle = ev => {
this.setState({
// 不管状态是否改变,都会控制render重新渲染
});
}
// 手动对比优化,与自动对比同时出现时手动为主
shouldComponentUpdate(nextProps, nextState) {
// 拿当前的状态和最新修改的状态进行对比(浅对比),如果一样则不渲染,不一样才进行渲染
if (this.state.n === nextState.n) {
return false;
}
return true;
}
// 自动对比
export default class Test extends React.PureComponent {
...
}
...
-
setState
本身在生命周期函数或者合成事件中执行是异步的
=>保证REACT生命周期函数执行的顺序不会紊乱
=>保证其实现渲染队列的机制,可以合并setState
后统一处理
export default class Test1 extends React.Component {
state = {
n: 0
};
render() {
console.log("render")
return <div>
{this.state.n}
<button onClick={this.handler}>+</button>
</div>
}
handler = ev => {
this.setState({
n: this.state.n + 1
})
console.log('ok')
}
}
点击按钮后
-
setState
在原生事件绑定中和其他异步操作中是同步的
=>此时失去渲染队列的效果
export default class Test1 extends React.Component {
state = {
n: 0,
m: 0
};
render() {
console.log('render')
return <div>
{this.state.n} === {this.state.m}
<button onClick={this.handler}>+</button>
</div>
}
handler = ev => {
setTimeout(()=>{
this.setState({
n: 10
})
this.setState({
m:20
})
console.log('ok')
},1000)
}
}
失去渲染队列处理效果
- 按理说ES6中的类是不能设置静态属性的,但是WEBPACK打包编译的时候会根据
babel-preset-react
将其转换为复合规范的语法
// 以下写法理应报错,但是webpack编译后可正常运行
...
static defaultProps = {
m: 100
};
static propTypes = {
m: PropTypes.number,
x: PropTypes.string.isRequired
};
...
- 通过
onChange
事件实现MVVM双向绑定
...
<input type="text" className='form-control'
value={text}
onChange={ev => {
this.setState({
text: ev.target.value
});
}}/>
...
4.生命周期
1.第一次调用组件渲染的周期流程
1.1.给属性设置默认值(设置默认规则)
1.2.constructor
=> 设置初始的状态等
1.3.componentWillMount
第一次挂载之前 => 向服务器发送数据请求
1.4.render
渲染
1.5.componentDidMount
第一次挂载之后 => 把虚拟DOM转换为真实DOM了,我们可以获取DOM元素进行操作
2.当组件状态发生改变 setState
2.1.shouldComponentUpdate(nextProps, nextState)
是否允许当前组件重新渲染(返回TRUE则继续重新渲染,返回FALSE则停止重新渲染)
2.2.componentWillUpdate(nextProps, nextState)
重新渲染之前
2.3.render
重新渲染
2.4.componentWillUpdate
重新渲染之后
3.当组件属性发生改变:父组件重新传递最新的属性信息
3.1.componentWillReceiveProps(nextProps, nextState)
在接受最新的属性之前
3.2....(重复2)
4.componentWillUnmount
卸载组件之前
export default class Test extends React.Component {
constructor(props) {
super(props);
console.log('constructor');
this.state = {
data: [],
n: 0
};
}
componentWillMount() {
console.log('componentWillMount');
setTimeout(() => {
this.setState({
data: [100, 200]
});
}, 5000);
}
render() {
console.log('render');
let { data, n } = this.state;
return <div>
{data} === {n}
<button onClick={() => {
this.forceUpdate(); <!-- 调用该方法时直接跳过shouldComponentUpdate阶段 -->
}}>强制更新</button>
</div>;
}
componentDidMount() {
console.log('componentDidMount');
}
shouldComponentUpdate(nextProps, nextState) {
console.log('shouldComponentUpdate', nextProps, nextState);
// nextProps nextState 最新要修改的属性和状态
// this.state / this.props 修改之前的
// this.forceUpdate() 不会执行这个周期函数,会强制更新当前组件
return true;
}
componentWillUpdate() {
console.log('componentWillUpdate');
}
componentDidUpdate() {
console.log('componentDidUpdate');
}
}
网友评论