两种方式创建Class组件
ES5方式(过时)
import React from 'react'
const A = React.createClass({
render(){
return (
<div>hi</div>
)
}
})
export default A
// 由于 ES5 不支持 class,才会有这种方式
ES6 方式
class B extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>hi</div>
)
}
}
export default B
// extends,constructor,super 强行记忆
以后只用class方式创建类组件
webpack+babel将ES6翻译成ES5
Props-外部数据
一般外部数据都是来自父元素的属性
传入props给B组件
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = { name: 'ories' }
}
onClick = ()=> {}
render() {
return <B name={this.state.name} onClick={this.onClick}>hi</B>
}
}
export default B
// name, onClick就是外部数据
// 外部数据被包装为一个对象, {name: 'frank', onClick:..., children:'hi'}
// 此处的 onClick 是一个回调
props初始化
初始化
class B extends React.Component {
constructor(props){
super(props);
}
render(){}
}
- 要么不初始化,即不写constructor
- 要么初始化,必须写全,不写super直接报错
- 这么做了之后,this.props就是外部数据对象的地址了
读取props
读取
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>
)
}
}
// 通过 this.props.xxx 读取
不准写Props
-
给props的值(一个地址)
-
this.props = {/另一个对象/}
-
不要写这样的代码,没有意义
-
理由: 既然是外部数据,就应该由外部更新
-
该props的属性
-
this.props.xxx = 'hi'
-
不要写这样的代码,没有意义
-
理由: 既然是外部数据,就不应该从内部改值
-
原则
-
应该由数据的主人对数据进行更改
相关钩子
component WillReceiveProps钩子
当组件接受新的props时,会触发此钩子
钩子就是特殊函数,属于行业黑话
例子
import React from "react";
import ReactDOM from "react-dom";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {x: 1}
}
onClick = ()=>{
this.setState({
x: this.state.x + 1
})
}
render(){
return <div className="App">
App <button onClick={this.onClick}>+1</button>
<B name={this.state.x}></B>
</div>
}
}
class B extends React.Component {
componentWillReceiveProps(newProps, nextContext) {
console.log('旧的 props', this.props)
console.log('props变化了')
console.log('新的props')
console.log(newProps)
}
render(){
return <div>
{this.props.name}
</div>
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
- 该钩子已经被弃用,总之,不要使用这个钩子
props的作用
- 接受外部-数据
- 只能读不能写
- 外部数据由父组件传递
- 接受外部-函数
- 在恰当的时机,调用该函数
- 该函数一般是父组件的函数
内部数据State
- State & setState
- 初始化State,代码
class B extends React.Component {
constructor(props) {
super(props);
this.state = {
user: {name: 'frank', age: '18'}
}
}
render() {/* ... */}
}
读写State
读用this.state, this.state.xxx.yyy.zzz
写用this.setState(???, fn)
注意setState不会立即改变this.state,会在当前代码运行完后,再去更新this.state,从而触发UI更新
this.setState((state, props)=> newState, fn)
以上的代码反而更容易理解state
fn会在写入成功后执行
写的时候会shallow merge, setState会自动将新state与旧state进行一级合并
改写+1的例子,set的时候尽量用函数的形式,能用就用
import React from "react";
import ReactDOM from "react-dom";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {x: 1}
}
onClick = ()=>{
this.setState({
x: this.state.x + 1
})
this.setState({
x: this.state.x + 1
})
// 只会加1
}
onClick2 = ()=>{
this.setState((state)=>({x: state.x + 1}))
this.setState((state)=>({x: state.x + 1}))
// 会加2
}
render(){
return <div className="App">
App <button onClick={this.onClick2}>+1</button>
<B name={this.state.x}></B>
</div>
}
}
class B extends React.Component {
componentWillReceiveProps(newProps, nextContext) {
console.log('旧的 props', this.props)
console.log('props变化了')
console.log('新的props')
console.log(newProps)
}
render(){
return <div>
{this.props.name}
</div>
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
生命周期
- 类比的如下代码
let div = document.createElement('div')
//这是div的create/construct过程
div.textContent='hi'
// 这是初始化state
document.body.appendChild(div)/
//这是div的mount过程
div.textContent = 'hi2'
//这是div的update过程
div.remove()
// 这是div的unmount过程
同理react组件也有这些过程,我们称之为生命周期
react有如下生命周期
函数列表,带的重要,不带的不常用,
*constructor() //构造初始化
static getDerivedStateFromProps()
*shouldComponentUpdate() //组件确认更新修改
*render() //组件渲染
getSnapshotBeforeUpdate()
*componentDidMount() //组件挂载
*componentDidUpdate() //组件被更新之后
*componentWillUnmount() //组件将要卸载
static getDerivedStateFromError()
componentDidCatch()
React常用生命周期
函数列表
constructor()-在这里初始化state
shouldComponentUpdate()-return false阻止更新
render()-创建虚拟dom
componentDidMount()-组件已出现在页面
componentDidUpdate()-组件已更新
componentWillUnmount()-组件将死
constructor
- 用途:
初始化props
初始化state,但此时不能调用setState
用来写bind this
constructor() {
/* 其他代码略 */
this.onClick = this.onClick.bind(this)
可以用新语法代替
onClick = ()=> {}
constructor(){ /* 其他代码略 */ }
}
shouldComponentUpdate
- 用途
返回true表示不阻止UI更新
返回false表示阻止UI更新
- 面试常问: shouldComponentUpdate有什么用?
答: 它允许我们手动判断是否要进行组件更新,我们可以根据应用场景灵活地设置返回值,以避免不必要的更新
这样写render会重复执行
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
n: 1
}
}
onClick = ()=>{
this.setState(state=> ({
n: state.n+1
}))
this.setState(state=> ({
n: state.n-1
}))
}
render(){
console.log('render了一次')
return <div>
App
<div>
{this.state.n}
<button onClick={this.onClick}>+1</button>
</div>
</div>
}
}
这样写render就不会重复执行
...以上代码省略
shouldComponentUpdate(newProps, newState) {
if(newState.n === this.state.n) {
return false
} else {
return true
}
}
render(){
console.log('render了一次')
return <div>
App
<div>
{this.state.n}
<button onClick={this.onClick}>+1</button>
</div>
</div>
}
}
- 启发,所有组件是否都要加这个功能
React.PureComponent,新旧state做一层浅对比,以及新 props 和旧 props 的每一个 key。
如果所有 key 的值全都一样,就不会 render;如果有任何一个 key 的值不同,就会 render。
大多数情况下能用PureComponent,就尽量用PureComponent,除非遇到了bug
生命周期render用途
- 展示视图,return(<div>...</div>),return一个虚拟dom
- 只能有一个根元素
- 如果有两个根元素,就要用<React.Fragment>包起来,<React.Fragment>可以缩写成<></>
生命周期render技巧
- render里面可以写if...else
- render里面可以写?:表达式
- render里面不能写for循环,需要用数组
- render里面可以写array.map(循环)
生命周期componentDidMount()用途(组件挂载之后的操作,也是请求ajax的地方)
- 在元素插入页面后执行代码,这些代码依赖DOM
- 比如获取div的高度
- 此处可以发起加载数据的AJAX请求
- 首次渲染会执行此钩子
- 参数没有
react提供了更方便的方式获取div
this.divRef = React.createRef()
<div ref={this.divRef}>hi</div>
componentDidMount(){
const div = this.divRef.current
}
生命周期componentDidUpdate()的用途
- 在视图更新后执行代码
- 此处也可以发起AJAX请求,用于更新数据
- 首次渲染不会执行此钩子
- 在此处setState可能会引起无限循环,除非放在if里
- 若shouldComponentUpdate反回false,则不触发此钩子
- 接受之前的props,之前的state,和snapshot参数一般不用
componentWillUnmount的用途
- 组件将要被移出页面然后被销毁时执行的代码
- unmount过的组件不会再次mount
componentWillUnmount举例
如果在componentDidMount里面监听了window scroll,那么就要在componentWillUnmount里面取消监听
如果在componentDidMount里面创建了Timer,就要在componentWillUnmount里面取Timer
如果在componentDidMount里面创建AJAX请求,就要在componentWillUnmount里面取消请求,请求没回来就关闭页面
总结钩子
constructor() - 在这里初始化state
shouldComponentUpdate() - return false 阻止跟新
render() - 创建虚拟DOM
componentDidMount() - 组件已经出现在页面里面
componentDidUpdate() - 组件已更新
componentWillUnmount() - 组件将死
钩子的渲染过程图
React钩子机制.jpg
网友评论