学习资源:张天禹老师(尚硅谷 React 教程)
this
指向问题ES6
语法知识(class
类)npm
包管理器Javascript
库Facebook
开发且开源 Jordan Walke
DOM
相关的 API
来操作 UI
效率低DOM
浏览器会产生大量的重绘重排document.getElementById('app')
document.getquerySelector('#app')
document.getElementByTagName('div')
声明式编码便于提高开发效率和组件复用率
在 React Native
可以使用 React
语法进行移动端开发
使用虚拟 DOM
和优秀的 Diffing
算法,尽量减少与真实 DOM
的交互
...
babel.min.js
ES6
语法转成 ES5
语法jsx
转成 js
react.development.js
(作为React
核心库)
react-dom.development.js
扩展库(用于操作DOM
)
react
核心库,再引入 react-dom
扩展库
<div id="app">div>
<script src="../js/react.development.js">script>
<script src="../js/react-dom.development.js">script>
<script src="../js/babel.min.js">script>
<script type="text/babel">
// 1.创建虚拟 DOM
const VDOM = <h1>Hello React</h1>
// 此处不写引号 因为不是字符串
// 2.渲染虚拟 DOM 到页面
// ReactDOM.render(虚拟 DOM,容器)
const app = document.getElementById('app')
ReactDOM.render(VDOM, app)
script>
DOM
的两种创建方式
jsx
与 js
创建方式比较// jsx 方式创建
const VDOM = (
<h1 id="title">
<span>Hello React</span>
</h1>
)
// javascript 方式创建
// 如果 DOM 有多层嵌套 那么 javascript 书写结构非常复杂
/*
const VDOM = React.createElement('h1', { id: 'title' }, React.createElement('span', {}, 'Hello React'))
// createElement 为创建 DOM 元素
// createElement(标签元素,标签属性,标签体内容)
*/
// 2.渲染虚拟 DOM 到页面
const app = document.getElementById('app')
ReactDOM.render(VDOM, app)
Object
类型对象React
转化为真实 DOM
呈现在页面中const VDOM = (
<h1 id="title">
<span>Hello React</span>
</h1>
)
console.log(VDOM);
console.log(typeof VDOM)
console.log(VDOM instanceof Object);
JavaScript XML
XML
早期用于存储和传输数据<student>
<name>Tomname>
<age>18age>
student>
jsx
是 React
定义的一种类似于 XML
的 JS
扩展语法
JS + XML
的组合React.createElement(component, prop, ...children)
方法的语法糖DOM
不能写引号 const VDOM = 'Hello React
' // 无效写法
js
表达式注意区分:不是
Vue
中的插值语法
const data = 'Hello React'
// 创建虚拟 DOM
const VDOM = (
<h1 id="title">
<span>{data}</span>
// 书写 js 表达式
<span>{data.toLowerCase()}</span>
</h1>
)
// 渲染虚拟 DOM 到页面
const app = document.getElementById('app')
ReactDOM.render(VDOM, app)
样式应用书写
className
而不是 class
style = {{ key:value }}
形式const VDOM = (
<div className="react-demo">
// 渲染时为 class="react-demo"
<div style={{color: 'pink'}}>
Hello React!
</div>
</div>
)
jsx
不允许有多个根标签(只有一个根标签)
标签必须闭合
标签首字符
html
同名元素,若 html
中无该标签对应的同名元素则,则报错React
渲染对应的组件,若组件没有定义,则报错所以在
React
中使用某种组件时需要其首字母大写!
const VDOM = (
<div>
<h1 id="title" className="pink">
<span style={{color: 'white', font-size: '29px'}}>{data}</span>
<span>{data.toLowerCase()}</span>
</h1>
<h1>Hello React</h1>
<input type="text" />
</div>
)
<style>
.pink {
background-color: pink;
}
</style>
const data = 'Hello React'
// 创建虚拟 DOM
const VDOM = (
<h1 id="title" className="pink">
<span style={{color: 'white', font-size: '29px'}}>{data}</span>
<span>{data.toLowerCase()}</span>
</h1>
)
// todo: 渲染虚拟 DOM 到页面
注意
js
表达式与js
语句区分!
jsx
注释 { /* ... */ }
const VDOM = (
<h1 id="title" className="pink">
<span>{data.toLowerCase()}</span>
{/*{data.toUpperCase()}*/}
</h1>
)
jsx
小练习使用
React
渲染数据datas
到页面中
//
// 注意写在 babel.min.js 中哦
// 模拟数据
const datas = ['Angular', 'React', 'Vue']
const renderDatas = datas.map((data, index) => <li key={index}> {data} </li>)
// console.log(renderDatas);
// 创建虚拟 DOM
const VDOM = (
<div>
<h1>前端 js 框架</h1>
<ul>
{renderDatas}
</ul>
</div>
)
// 渲染虚拟 DOM 到页面
ReactDOM.render(VDOM, document.getElementById('app'))
React Developer Tools
Function
// 1. 创建组件 函数式写法
function MyComponent() {
// 使用了严格模式 use strict 因为 babel 编译后默认开启严格模式
// console.log(this); // 函数内部 this 指向为 undefined
return <h1>函数定义的组件</h1>
}
// 2. 渲染组件到页面
// 第一个参数为自定义标签(函数式组件名称)
ReactDOM.render(<MyComponent />, document.
getElementById('app'))
ReactDOM.render(...)
做了什么?
ReactDOM
会调用 MyComponent
函数,对于 render
函数的第一个参数,React
解析为组件标签,然后会找到 MyComponent
组件DOM
转为真实 DOM
Class
// 1. 创建类式组件 均需要继承 React.component 父类
class MyComponent extends React.Component {
// render 为类 MyComponent 的原型对象上的方法,供实例使用
render() {
console.log(this); // MyComponent 的实例对象
return <h1>Hello React</h1>
}
}
ReactDOM.render(<MyComponent />, document.getElementById('app'))
ReactDOM.render(...)
做了什么?
React
解析组件标签找到 MyComponent
组件new
创建该类的render
方法render
返回的虚拟 DOM
转为真实 DOM
随后呈现在页面上组件被称为状态机,通过更新组件的 state
来更新对应的页面显示(重新渲染组件)
state
是组件对象最重要的属性,是对象
可以包含多个 key-value
的组合
state
只能是 object
或 null
class Weather extends React.Component {
constructor(props) {
// 书写 constructor 后第一句需要写 super(...)
super(props)
// 初始化状态 state
this.state = { isHot: true }
}
render() {
// console.log(this);
// this 指向类 Weather 的实例对象 因为本质上是 Weather 类的实例对象调用 render 方法
return <h1>今天天气{this.state.isHot ? '炎热' : '凉爽'}</h1>
}
}
// todo: ReactDOM.render()
回顾原生实现事件绑定(点击事件…)
addEventListener
监听事件btn.addEventListener('click', callback)
onclick
btn.onclick = callback
// 或为 dom 元素设置属性
//
// function test () { //... }
React
完成事件绑定
注意:类中的方法默认开启局部的严格模式
this
指向问题
class Weather extends React.Component {
constructor(props) {
super(props)
// 初始化状态
this.state = { isHot: true }
}
demo() {
// 结果 this 为 undefined 而不是 Weather 实例对象
console.log(this)
}
render() {
return (
<div>
{/* 注意是 onClick 【不是】 onclick */}
<h1 onClick={this.demo}>
今天天气{this.state.isHot ? '炎热' : '凉爽'}
</h1>
</div>
)
}
}
// todo: ReactDOM.render(...)
为什么
this
为undefined
?
// Example
var date = new Date();
date.getTime() // 1677578203663
var print = date.getTime;
print() // Uncaught TypeError: this is not a Date object.
date.getTime()
方法赋给变量 print
,然后调用 print()
就报错了。这是因为 getTime()
方法内部的 this
,绑定 Date
对象的实例,赋给变量print
以后,内部的 this
已经不指向 Date
对象的实例
this.demo
也是同样的道理!内部的this
不再指向组件实例对象!
this
指向
bind
(不推荐,滥用原型链)class Weather extends React.Component {
constructor(props) {
super(props)
// 初始化状态
this.state = { isHot: true }
// 改变 this 指向 使用 bind
// 在实例上添加 demo
this.demo = this.demo.bind(this)
}
demo() {
// demo 方法在类的原型对象上 供实例去使用
console.log(this)
}
/*
demo = () => {
// 通过 Weather 实例调用 demo demo 中的 this 指向 Weather 实例
// 只能写成箭头函数 不然 this 指向为 undefined
console.log(this);
}
*/
render() {
// console.log(this);
return (
<div>
<h1 onClick={this.demo}>今天天气{this.state.isHot ? '炎热' : '凉爽'}</h1>
</div>
)
}
}
setState
的使用
state
状态state
class Weather extends React.Component {
constructor(props) {
super(props)
// 初始化状态
this.state = { isHot: true }
}
demo = () => {
console.log(this);
// 不起作用 但 state 值修改了【视图】没更新 DOM
this.state.isHot = !this.state.isHot
}
// todo render ...
}
// todo...
setState
修改class Weather extends React.Component {
constructor(props) {
super(props)
// 初始化状态
this.state = { isHot: true }
this.demo = this.demo.bind(this)
}
demo() {
const isHot = this.state.isHot
this.setState({
isHot: !isHot
})
}
// todo render ...
}
// todo...
this.setState
可合并多个状态操作Weather
类构造器调用 1 次,成员方法 render
调用 1 + n
次
state
的简写方式class Weather extends React.Component {
// 初始化状态
// 给实例添加属性 state
state = { isHot: true }
// 自定义方法
demo = () => {
console.log(this);
const { isHot } = this.state
this.setState({ isHot: !isHot })
}
render() {
return (
<div>
{/* this.demo 仅仅是保存的 demo 方法 实际上 this 发生改变 */}
<h1 onClick={this.demo}>今天天气
{this.state.isHot ? '炎热' : '凉爽'}</h1>
</div>
)
}
}
// 创建组件
class Person extends React.Component {
render() {
// 实例内部使用 props 接受
const { name, sex, age } = this.props
return (
<ul>
<li>姓名 {name}</li>
<li>性别 {sex}</li>
<li>年龄 {age}</li>
</ul>
)
}
}
// 渲染组件 Person 标签内部传入属性
ReactDOM.render(<Person name="张三" age="20" sex="男" />, document.getElementById('app'))
// 创建组件
class Person extends React.Component {
render() {
const { name, sex, age } = this.props
return (
<ul>
<li>姓名 {name}</li>
<li>性别 {sex}</li>
<li>年龄 {age}</li>
</ul>
)
}
}
const p = { name: "张三", age: 20, sex: "男" }
// 使用扩展运算符 ...
ReactDOM.render(<Person {...p} />, document.getElementById('app'))
自
React v15.5
起,React.PropTypes
已移入另一个包中请使用prop-types
库open in new window
代替
// 引入 `props-types` 用于对组件的标签属性进行限制
<script src="../js/prop-types.js"></script>
// 创建组件
class Person extends React.Component {
render() {
const { name, sex, age } = this.props
return (
<ul>
<li>姓名 {name}</li>
<li>性别 {sex}</li>
<li>年龄 {age}</li>
</ul>
)
}
}
// 对标签属性进行类型与必要性的限制
Person.propTypes = {
// 限制 name 字符串且必传
name: PropTypes.string.isRequired,
// 限制 sayHello 为函数
sayHello: PropTypes.func
}
// 指定默认标签属性值
Person.defaultProps = {
sex: '男'
}
function sayHello() {
console.log('hello')
}
const p = { name: "张三", age: 20, sex: "男", sayHello }
// 使用扩展运算符 ...
ReactDOM.render(<Person {...p} />, document.getElementById('app'))
props
的简写使用扩展运算符
...
props
的只读性
class
声明,都绝不能修改自身的 props
// 创建组件
class Person extends React.Component {
render() {
const { name, sex, age } = this.props
return (
<ul>
<li>姓名 {name}</li>
<li>性别 {sex}</li>
<li>年龄 {age}</li>
</ul>
)
}
// 需要添加 static 关键字 作为静态属性
static propTypes = {
// 限制 name 字符串且必传
name: PropTypes.string.isRequired,
// 限制 sayHello 为函数
sayHello: PropTypes.func
}
// 指定默认标签属性值
static defaultProps = {
sex: '男'
}
}
function sayHello() {
console.log('hello')
}
const p = { name: "张三", age: 20, sex: "男", sayHello }
// 使用扩展运算符 ...
ReactDOM.render(<Person {...p} />, document.getElementById('app'))
React
中,通常情况下,构造函数仅用于以下两种情况:
this.state
赋值对象来初始化 state
。this
指向)React
组件挂载之前,会调用它的构造函数。在为 React.Component
子类实现构造函数时,应在其他语句之前调用 super(props)
。否则,this.props
在构造函数中可能会出现未定义的 bug
。class Person extends React.Component {
// 基本上少写 constructor
constructor(props) {
// 构造函数是否接受 props,是否传递给 super 取决于是否希望在构造器中通过 this 访问 props
// debugger
/*
实际调用
function Component(props) {
// this ==> Person
this.props = props
}
*/
super(props)
console.log(this.props)
}
}
function Person(props) {
const { name, sex } = props
return (
<ul>
<li>姓名 {name}</li>
<li>性别 {sex}</li>
</ul>
)
}
Person.propTypes = {
name: PropTypes.string.isRequired,
}
//指定默认标签属性值
Person.defaultProps = {
sex: '男',
}
const p = { name: '张三' }
ReactDOM.render(<Person {...p} />, document.getElementById('app'))
ref
字符串形式(过时 API
)
ref
标识自己class Demo extends React.Component {
showData = () => {
const input = this.refs.ipt1
// console.log(input) // 可用于获取真实 DOM
console.log(input.value)
}
render() {
return (
<div>
<input ref="ipt1" type="text" />
<button onClick={this.showData}>点击</button>
</div>
)
}
}
// todo:ReactDOM.render( , ...)
ref
回调形式class Demo extends React.Component {
showData = () => {
console.log(this.input1.value);
}
render() {
return (
<div>
{/* 保存 ref 到实例对象上的 input1 属性 */}
<input ref={(c) => this.input1 = c} type="text" />
<button onClick={this.showData}>点击</button>
</div>
)
}
}
ReactDOM.render(<Demo />, document.getElementById('app'))
ref
回调形式调用次数问题
如果 ref
回调函数是以内联函数的方式定义的,在更新过程中它会被执行两次,第一次传入参数 null
,然后第二次会传入参数 DOM
元素
// 创建组件
class Demo extends React.Component {
state = { isHot: false }
showData = () => {
console.log(this.input1.value);
}
changeWeather = () => {
const { isHot } = this.state
this.setState({ isHot: !isHot })
}
render() {
const { isHot } = this.state
return (
<div>
<h2>今天天气{isHot ? '炎热' : '凉爽'}</h2>
<input ref={(c) => { this.input1 = c; console.log('@', c); }} type="text" placeholder="点击提示" />
<button onClick={this.showData}>点击展示数据</button>
<button onClick={this.changeWeather}>点击切换天气</button>
</div>
)
}
}
// 每次点击切换天气按钮 ==> 重新渲染(更新)
// @ null
// @ input
因为在每次渲染时会创建一个新的函数实例,所以 React
清空旧的 ref
并且设置新的 ref
通过将 ref
的回调函数定义成 class
的绑定函数的方式可以避免上述问题,但是大多数情况下它是无关紧要的
// 创建组件
class Demo extends React.Component {
state = { isHot: false }
showData = () => {
console.log(this.input1.value);
}
changeWeather = () => {
const { isHot } = this.state
this.setState({ isHot: !isHot })
}
// 写在函数体内
saveInput = (c) => {
this.input1 = c
console.log('@', c);
// 只会输出一次
}
render() {
const { isHot } = this.state
return (
<div>
<h2>今天天气{isHot ? '炎热' : '凉爽'}</h2>
<input ref={this.saveInput} type="text" placeholder="点击提示" />
<button onClick={this.showData}>点击展示数据</button>
<button onClick={this.changeWeather}>点击切换天气</button>
</div>
)
}
}
refs
myRef = React.createRef()
refs
this.myRef.current
class Person extends React.Component {
// React.createRef 调用后返回一个容器 该容器可以存储 【被 ref 所标识】 的节点
myRef = React.createRef()
showData = () => {
// 获取 input
// console.log(this.myRef.current);
// 获取 input 框的值
console.log(this.myRef.current.value);
}
render() {
return (
<div>
{/*会将 ref 所在【标签节点】存储在 myRef*/}
<input ref={this.myRef} type="text" />
<button onClick={this.showData}>点击</button>
</div>
)
}
}
通过 onXxx
属性指定事件处理函数(注意大小写)
React
使用自定义(合成)事件,而不是使用原生的 DOM
事件 —— 兼容性
React
中的事件是通过事件委托方式(基于冒泡)处理的(委托给组件最外层的元素)—— 高效
通过 event.target
得到发生事件的 DOM
元素对象
注意:不要过度使用
ref
class Person extends React.Component {
showData = (e) => {
console.log(e.target.value)
}
render() {
return (
<div>
<input onBlur={this.showData} />
</div>
)
}
}
类似于
vue
的双向数据绑定
React
中的 state
成为“唯一数据源”且使用 setState()
来更新,同时渲染表单的 React
组件还控制着用户输入过程中表单发生的操作,被 React
以这种方式控制取值的表单输入元素就叫做**“受控组件”**// 创建组件
class Person extends React.Component {
// 初始化状态
state = {
username: '',
password: ''
}
handleSumbit = (e) => {
e.preventDefault() // 阻止表单提交
const { username, password } = this.state
alert(`用户名为 ${username} 密码为 ${password}`);
}
getUsername = (event) => {
// console.log(event.target.value);
this.setState({ username: event.target.value })
}
getPassword = (event) => {
this.setState({ password: event.target.value })
}
render() {
return (
<form onSubmit={this.handleSumbit}>
用户名: <input type="text" onChange={this.getUsername} name="username" />
密码: <input type="password" onChange={this.getPassword} name="password" />
<button>登录</button>
</form>
)
}
}
// todo: ReactDOM.render( , ...)
ref
来从 DOM
节点获取表单数据class Person extends React.Component {
handleSumbit = (e) => {
e.preventDefault() // 阻止表单提交
const { username, password } = this // 从实例上获取 username 与 password
alert(`用户名为 ${username.value} 密码为 ${password.value}`);
}
render() {
return (
<form onSubmit={this.handleSumbit} >
用户名: <input ref={c => this.username = c} type="text" name="username" />
密码: <input ref={c => this.password = c} type="password" name="password" />
<button>登录</button>
</form>
)
}
}
// todo: ReactDOM.render( , ...)
class Person extends React.Component {
//初始化状态
state = {
username: '',
password: ''
}
handleSumbit = (e) => {
e.preventDefault() // 阻止表单提交
const { username, password } = this.state
alert(`用户名为 ${username} 密码为 ${password}`);
}
saveFormData = (dataType) => {
// console.log(dataType);
// 注意:返回的箭头函数才是作为 onChange 事件的回调
// dataType 输入类型为字符串,需要【中括号】包裹
return (event) => {
this.setState({ [dataType]: event.target.value })
}
}
render() {
return (
<form onSubmit={this.handleSumbit}>
用户名: <input type="text" onChange={this.saveFormData('username')} name="username" />
密码: <input type="password" onChange={this.saveFormData('password')} name="password" />
<button>登录</button>
</form>
)
}
}
// todo: ReactDOM.render( , ...)
saveFormData
返回一个箭头函数,作为 onChange
事件的监听函数,好处是通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式class Person extends React.Component {
// 初始化状态
state = {
username: '',
password: ''
}
handleSumbit = (e) => {
e.preventDefault() // 阻止表单提交
const { username, password } = this.state
alert(`用户名为 ${username} 密码为 ${password}`);
}
saveFormData = (event, dataType) => {
this.setState({ [dataType]: event.target.value })
}
render() {
return (
<form onSubmit={this.handleSumbit}>
用户名: <input type="text" onChange={(event) => this.saveFormData(event, 'username')} name="username" />
密码: <input type="password" onChange={(event) =>
this.saveFormData(event, 'password')} name="password" />
<button>登录</button>
</form>
)
}
}
ReactDOM.render(<Person />, document.getElementById('app'))
componentDidMount
DOM
树中)会立即调用
componentWillUnmount
// 创建组件
class Life extends React.Component {
state = { opacity: 1 }
// 挂载 mount 卸载 unmount
demo = () => {
// 卸载组件 API (unmountComponentAtNode)
ReactDOM.unmountComponentAtNode(document.getElementById('app'))
}
// 组件挂载后(插入 DOM 树中)立即调用
// 实例会调用 所以 this 指向 组件实例
componentDidMount() {
this.timer = setInterval(() => {
let { opacity } = this.state
opacity -= 0.1
if (opacity < 0) opacity = 1
this.setState({ opacity })
}, 200)
}
// 组件卸载及销毁之前立即调用
componentWillUnmount() {
// 清楚定时器
clearInterval(this.timer)
}
// render 调用时机 初始化渲染与状态更新之后
render() {
return (
<div>
<h1 style={{ opacity: this.state.opacity }}>React</h1>
<button onClick={this.demo}>点击</button>
</div>
)
}
}
// 渲染组件
ReactDOM.render(<Life />, document.getElementById('app'))
React
组件中包含一系列钩子函数(生命周期回调函数)会在特定的时刻调用
shouldComponentUpdate
shouldComponentUpdate()
返回 false
,则不会调用 render()
,默认为 true
可以想象该生命周期钩子为
render
“阀门”
forceUpdate()
用于强制让组件重新渲染
forceUpdate()
将致使组件调用 render()
方法,注意:此操作会跳过该组件的 shouldComponentUpdate()
// 创建组件
class Count extends React.Component {
// Count 构造器
constructor(props) {
console.log('1-constructor');
super(props)
// 初始化状态 state
this.state = { count: 0 }
}
add = () => {
const { count } = this.state
this.setState({ count: count + 1 })
}
// 强制更新按钮的回调
force = () => {
this.forceUpdate()
}
demo = () => {
// 卸载组件到对应的节点
ReactDOM.unmountComponentAtNode(document.getElementById('app'))
}
// 组件将要挂载的钩子
componentWillMount() {
console.log('2-componentWillMount');
}
// 组件挂载完毕的钩子
componentDidMount() {
console.log('4-componentDidMount');
}
// 组件将要卸载的钩子
componentWillUnmount() {
console.log('5-componentWillUnmount');
}
// setState 触发 用于控制组件更新的阀门,一般不写默认返回 true
shouldComponentUpdate() {
console.log('shouldComponentUpdate');
// return false // 生命周期中断 render 不会调用
return true
}
// 组件将要更新的钩子
componentWillUpdate() {
console.log('componentWillUpdate');
}
// 组件更新完毕的钩子
componentDidUpdate() {
console.log('componentDidUpdate');
}
// render 调用时机 初始化渲染与状态更新之后
render() {
console.log('3-render');
return (
<div>
<h1>count 的值为 {this.state.count}</h1>
<button onClick={this.add}>+1</button>
<button onClick={this.demo}>卸载组件</button>
<button onClick={this.force}>强制重新渲染组件</button>
</div>
)
}
}
// 渲染组件
ReactDOM.render(<Count />, document.getElementById('app'))
render
// 父组件 A
// 简单说明如何形成父子组件,后续会深入
// 在类式组件 A 的 render 中声明子组件 B 即可
class A extends React.Component {
render() {
return (
<div>
<div>A组件</div>
{/* 使用子组件 B */}
<B />
</div>
)
}
}
// 子组件 B
class B extends React.Component {
// 组件将要接收新的 props 的钩子
componentWillReceiveProps(props) {
console.log('B -- componentWillReceiveProps', props);
}
shouldComponentUpdate() {
console.log('shouldComponentUpdate');
return true
}
componentWillUpdate() {
console.log('componentWillUpdate');
}
componentDidUpdate() {
console.log('componentDidUpdate');
}
render() {
console.log('render')
return (
<div>B组件</div>
)
}
}
// 渲染组件
ReactDOM.render(<A />, document.getElementById('app'))
1. 初始化阶段 : 由 ReactDOM.render() 触发(初次渲染)
constructor()
componentWillMount()
render()
componentDidMount()
2. 更新阶段 : 由组件内部 this.setSate() 或父组件重新 render 触发
shouldComponentUpdate()
componentWillUpdate()
render()
componentDidUpdate()
3. 卸载组件 : 由 ReactDOM.unmountComponentAtNode() 触发
componentWillUnmount()
UNSAFE_name
(即将废弃)
UNSAFE_componentWillMount()
UNSAFE_componentWillUpdate()
UNSAFE_componentWillReceiveProps()
class Count extends React.Component {
// Count 构造器
constructor(props) {
}
// 强制更新按钮的回调
force = () => {
this.forceUpdate()
}
// 组件将要挂载的钩子
UNSAFE_componentWillMount() {
}
// 组件挂载完毕的钩子
componentDidMount() {
}
// 组件将要卸载的钩子
componentWillUnmount() {
}
// setState 触发 用于控制组件更新的阀门,一般不写默认返回 true
shouldComponentUpdate() {
// return false
return true
}
// 组件将要更新的钩子
UNSAFE_componentWillUpdate() {
}
// 组件更新完毕的钩子
componentDidUpdate() {
}
// render 调用时机 初始化渲染与状态更新之后
render() {
return (
<div>
{ /**/ }
</div>
)
}
}
UNSAFE_
前缀。unsafe
不是指安全性,而是表示使用这些生命周期的代码在 React
的未来版本(18.x
)中更有可能出现 bug
,尤其是在启用异步渲染之后getDerivedStateFromProps
state
的值在任何时候都取决于 props
(使用场景较少)getSnapshotBeforeUpdate
DOM
中捕获一些信息,此生命周期方法的任何返回值将作为参数传递给 componentDidUpdate()
class Count extends React.Component {
// Count 构造器
constructor(props) {
console.log('constructor');
super(props)
// 初始化状态 state
this.state = { count: 0 }
}
add = () => {
const { count } = this.state
this.setState({ count: count + 1 })
}
// 强制更新按钮的回调
force = () => {
this.forceUpdate()
}
// 译为从 【Props】 中获取派生的 state 的钩子,需要声明成【静态】方法且必须返回 state object 或 null
static getDerivedStateFromProps(Props) {
console.log('getDerivedStateFromProps',Props);
return null
// return { count: 2 } // 会影响 state.count 的变更 永远是 2
}
// 需要返回一个 snapshot value 或 null
getSnapshotBeforeUpdate() {
console.log('getSnapshotBeforeUpdate');
return null
}
// 组件挂载完毕的钩子
componentDidMount() {
}
// 组件将要卸载的钩子
componentWillUnmount() {
}
// setState 触发 用于控制组件更新的阀门,一般不写默认返回 true
shouldComponentUpdate() {
}
// 组件更新完毕的钩子
componentDidUpdate(preProps, preState, snapShotValue) {
// preState 是先前的 state,preProp 是先前的 props
console.log('componentDidUpdate', preProps, preState, snapShotValue)
}
// render 调用时机 初始化渲染与状态更新之后
render() {
console.log('render');
return (
<div>
<h1>count 的值为 {this.state.count}</h1>
<button onClick={this.add}>+1</button>
<button onClick={this.force}>强制重新渲染组件</button>
</div>
)
}
}
getSnapshotBeforeUpdate
的使用示例(使用场景较少)class NewList extends React.Component {
// 初始化 state
state = { newsArr: [] }
// 在组件更新之前获取 DOM 数据
getSnapshotBeforeUpdate() {
// 传递给 componentDidUpdate 作为参数
return this.refs.list.scrollHeight
}
// 组件挂载完毕调用钩子
componentDidMount() {
setInterval(() => {
// 获取原 state
const { newsArr } = this.state
// 模拟生成新闻数据
const news = '新闻' + (newsArr.length + 1)
// 更新 state
this.setState({ newsArr: [news, ...newsArr] })
}, 1000)
}
componentDidUpdate(preProps, preState, height) {
this.refs.list.scrollTop += this.refs.list.scrollHeight - height
console.log(this.refs.list.scrollTop);
}
render() {
return (
<div className="list" ref="list">
{this.state.newsArr.map((item, index) => {
return <div className="news" key={index}>{item}</div>
})}
</div>
)
}
}
ReactDOM.render(<NewList />, document.getElementById('app'))
TO BE CONTINUE ...