创建方式
class A extends React.Component {
constructor(props){
super(props);
}
render() {
return (
<div>hi</div>
)
}
}
export default A;
props外部数据
- 外部数据被包装成一个对象,B 组件接收到的 props 的值是:
{name:'frank, onClick:..., children:'hi'}【其中onClick是回调函数】
代码示例:
class Parent eztends React.Component{
constructor(props){ // 初始化,没有第4行的话2-5行可省略不写
super(props);
this.state = {name:'frank'}
}
onClick = ()=>{}
render(){
return <B name={this.state.name} onClick={this.onClick}>hi</B>
}
}
- this.props.xxx读取
class B extends React.Component {
constructor(props) {
super(props);
}
render(){
return <div onClick={this.props.onClick}>
{this.props.name}
<div>
{this.props.children}
</div>
</div>
}
}
- props只读不写 因为props保存的是值的地址。props是外部数据,就应该由外部数据更新(数据的主人对数据进行更改)
state内部数据
- 初始化state
class B extends React.Componrnt{
construcyor(props){
super(props);
this.state = {
user: {name:'frank', age:18}
}
}
render(){}
}
- 读写state 读用this.state.xxx
写用this.setState(newState, fn) 【fn是回调函数】
setState后不会立马改变this.state,会在当前代码运行完后再去更新this.state,从而触发更新UI,推荐函数的写法this.setState((state,props) => newState,fn)
bug示例:
- onClick直接修改对象,导致点击+1按钮以后x只加了一次
- onClick2写成函数,点击一次+1按钮让x成功的一次性加了两个1
- onClick写成回调嵌套,点击一次+1按钮让x逐步加了两个1
setState 的 shallow merge:
React 只会检查新 state 和旧 state 第一层的区别,并把新 state 缺少的数据从旧 state 里拷贝过来
生命周期(函数)
函数列表:
constructor() // 初始化state
static getDerivedStateFromProps()
shouldComponentUpdate() // return false阻止更新
render() // 创建虚拟DOM
getSnapshotBeforeUpdate()
componentDidMount // 组件已经吹出现在页面
componentDidUpdate() // 组件已更新
componentWillUnmount() // 组件将死
staticgetDerivedStateFromError()
componentDidcatch()
construtor
用途:
- 初始化props
- 初始化state,但此时不调用setState
- 用来写bind this
constructor(){
this.onClick = this.onClick.bind(this)
}
// 新语法代替
onClick = ()=> {}
constructor(){}
shouldComponentUpdate
用途:
- 返回true表示不阻止UI更新
- 返回false表示阻止UI更新
React内置了对比更新的函数React.PureComponent来代替React.Component,但它只会对比最外边的一层(浅对比)来决定是否render。
PureComponent 会在 render 之前对比新 state 和旧 state 的每一个 key,以及新 props 和旧 props 的每一个 key。
如果所有 key 的值全都一样,就不会 render;如果有任何一个 key 的值不同,就会 render。
质疑:为什么要用新对象而不再this.state身上直接改?因为这是React的编程哲学
示例代码:
class App extends React.Component{ // 可换为React.PureComponent代替should生命周期函数
constructor(props){
super(props);
this.state = {
n: 1
};
}
onClick = ()=> {
this.setState(state => ({n: state.n + 1}));
this.setState(state => ({n: state.n - 1}));
}
shouldComponentUpdate(newProps,newState){
if(newState.n === this.state.n){
return false
}else{
return true
}
}
render(){
console.log('render了一次') // 只render了一次
return (
<div>
{this.state.n}
<button onClick={this.onClick}>+1-1</button>
</div>
)
}
render
用途:
- 展示视图,return一个虚拟DOM(就是一个对象)
- 只能有一个根元素
- 如果有两个根元素,就要在外层再加一个div或者用
Reac.Fragment包起【可缩写成<></>】。两者的区别是:后者只是做一个占位符,不会真正出现
注意:
- render里可写if else
- render里可写? :表达式
- render里不能写for循环,需要用数组(否则只渲染第一个值)
- render里可写array.map循环【记得加key!】(曾经的疑问:为什么不直接输出创建的数组?当然可以了,但那样不就无法进行其他操作了吗,而且react推荐我们用创建新对象)
三目运算符
for循环以后遍历push入新数组
map遍历数组并塞入标签(加key,否则warning)
componentDidMount(挂载后执行的钩子)
用途:
- 在元素插入页面后执行的代码,这些代码依赖DOM【获取div高度】
- 发起加载数据的AJAX请求(官方推荐把请求放在此处钩子)
- 首次渲染会执行此钩子
获取div高度(不能写在constructor里,因为此时页面未)还可以使用ref,后面讲
componentDidUpdate
用途:
- 在视图更新后执行此代码
- 发起AJAX请求,用于更新数据
- 首次渲染不会执行此钩子
- 此处setState可能会引起无限循环(互相调用),除非放在if里
- 若shouldComponentUpdate返回false,则不会触发此钩子
参数:看文档
componentWillUnmount
用途:
- 组件将要被移出页面然后销毁时执行代码
- unmount过的组件不会再次mount
举例:(防止占内存,其实可不写)
- 如果你在
c..Didmount里监听了window scroll,那么你就要在c..DidUnmount里取消监听 - 如果你在
c..Didmount里监听了Timer,那么你就要在c..DidUnmount里取消Timer - 如果你在
c..Didmount里监听了AJAX,那么你就要在c..DidUnmount里取消请求
分阶段看钩子执行顺序:
首次渲染:创建-->渲染-->挂载
再次渲染:内/外部数据变化,forceUpdate(看文档)-->触发Should...Update-->往下进行流程判断
销毁:可以不做这件事
React弃用的生命周期钩子
1、componentWillMount
2、componentWillUpdate
3、componentWillReceiveProps