React知识点扩展
setState(修改数据)
- 对象式的setState
- stateChange是修改后的数据对象
- callback是可选的回调函数
javascript
setState(stateChange,[callback])
javascript
import React, { Component } from 'react'
export default class Demo extends Component {
state = { count: 0 }
add = () => {
const { count } = this.state
this.setState({ count: count + 1 },()=>{
console.log('输出', this.state.count) // 1
})
console.log('输出效果', this.state.count) // 0,原因this.setState是异步的
}
render() {
const { count } = this.state
return (
<div>
<h1>当前数字:{count}</h1>
<button onClick={this.add}>加</button>
</div>
)
}
}
- 函数式的setState
- 不用获取原来的state值
- 新状态依赖原状态的时候,建议使用
- updater可以接收state, props
- callback是可选的回调函数
javascript
setState(updater,[callback])
javascript
loss = () => {
this.setState(
(state, props) => {
return {
count: state.count - 1,
}
},
() => {
console.log('输出', this.state.count) // -1
}
)
console.log('输出效果', this.state.count) // 0
}
lazyLoad(懒加载)
- 路由组件的lazyLoad
- 效果:只有点击的时候才会加载,重复点击不会重复加载
javascript
步骤1:import { lazy } from 'react'
const Tab1 = lazy(() => import('./components/tab1'))
const Tab2 = lazy(() => import('./components/tab2'))
- 使用即可
javascript
<Routes>
<Route path="/tab1" element={<Tab1 />}></Route>
<Route path="/tab2/*" element={<Tab2 />}></Route>
<Route path="*" element={<Navigate to="/tab1" replace />} />
</Routes>
Hooks
- hooks是react16.8版本增加的新特性
- 目的:可以让你在函数组件中使用state以及其它的react特性
(内容存取)React.useState()
- 第一次初始化指定的值在内部作缓存
- 返回值,包含两个元素的数组,第一个未内部当前状态,第2个为更新状态值的函数
- Demo函数执行1+n次

javascript
import React, { useState } from 'react'
function Demo() {
const [name, setName] = useState('乞力马扎罗')
const [info, setNameInfo] = useState({
age: '18',
phone: '18847861437',
})
const changeName = () => {
// 写法一,参数是非函数值
setName('罗曼蒂克')
// 写法二,参数是函数
setName((name) => (name = '罗曼蒂克1'))
}
return (
<div>
<h4>名称:{name}</h4>
<h4>年龄:{info.age}</h4>
<h4>手机号:{info.phone}</h4>
<button onClick={changeName}>修改名称</button>
</div>
)
}
export default Demo
(模拟生命周期和监听)React.useEffect
- 可以让你在函数组件中用于模拟类组件的生命周期钩子
- 可以把useEffect Hook 看作如下三个函数的组合
-
- componentDidMount()
-
- componentDidUpdate()
-
- componentWillUnmount()
知识点
- 定时器的使用和卸载
- 用函数式更新,避免依赖旧值


javascript
import React, { useState, useEffect } from 'react'
function Demo() {
const [name, setName] = useState('乞力马扎罗')
const [info, setNameInfo] = useState({
age: 18,
phone: '18847861437',
})
// useEffect(() => {
// console.log('1+n次,页面数据更新几次执行几次')
// })
// useEffect(() => {
// console.log('监测name,name更新几次执行几次次')
// }, [name])
// useEffect(() => {
// console.log('监测name和 info.age,他们更新几次执行几次次')
// }, [name, info.age])
useEffect(() => {
console.log('谁也不监测,执行一次,适合存放定时器')
let timer = setInterval(() => {
setNameInfo((prevInfo) => {
// 用函数式更新,避免依赖旧的info
return { ...prevInfo, age: prevInfo.age + 1 }
})
}, 1000)
//return 这个相当于类组件的在组件卸载前执行
return () => {
clearInterval(timer)
}
}, [])
const changeName = () => {
setName('罗曼蒂克')
}
const changeAge = () => {
setNameInfo({
...info,
age: info.age + 1,
})
}
const changeComponts = () => {}
return (
<div>
<h4>名称:{name}</h4>
<h4>年龄:{info.age}</h4>
<h4>手机号:{info.phone}</h4>
<button onClick={changeName}>修改名称</button>
<button onClick={changeAge}>修改年龄</button>
<button onClick={changeComponts}>卸载组件</button>
</div>
)
}
export default Demo
(获取ref的值)React.useRef()

javascript
import React, { useRef } from 'react'
function Demo() {
const myRef = React.useRef()
const changeTip = () => {
alert(myRef.current.value)
}
return (
<div>
<input type="text" ref={myRef} />
<button onClick={changeTip}>点击提示信息</button>
</div>
)
}
export default Demo
Fragment(标签)
- 目的,用于减少div的渲染次数,这个标签在渲染过程中会忽略
- Fragment只能拥有一个属性,key
- 可省略Fragment,但是省略后,将不能写key
javascript
import React, { Fragment } from 'react'
function Demo() {
return (
//可省略 <></>
<Fragment key="1">
<div>1111</div>
</Fragment>
)
}
export default Demo
React 组件通信种类

Context(组件通信)
- 目的:一种组件通信方式, 常用于祖组件和后代组件之间的通信
- 开发中一般不用,一般都用于封装react插件

javascript
import React, { Component } from 'react'
import './index.css'
// 步骤1:创建Context容器对象
const MyContext = React.createContext()
const { Provider, Consumer } = MyContext
class A extends Component {
state = { name: '乞力马扎罗', age: 18 }
render() {
return (
<div className="pd20">
<h4>祖先组件,Context示例</h4>
<h4>内容值:{this.state.name}</h4>
{/* <B /> */}
{/* 步骤2,从容器对象中获取Provider包裹组件,且必须用value传递值 */}
<Provider value={{ name: this.state.name, age: this.state.age }}>
<B />
</Provider>
</div>
)
}
}
class B extends Component {
render() {
return (
<div className="pd20">
<h5>父组件</h5>
<h5>从祖先组件获取值:</h5>
<C />
<D />
</div>
)
}
}
// 类组件接收
class C extends Component {
// 步骤3,类组件中得声明接收使用
static contextType = MyContext
render() {
console.log(this.context, '祖先存储的值')
return (
<div className="pd20">
<h5>类子组件</h5>
<h5>从祖先组件获取值:{this.context.name}</h5>
</div>
)
}
}
// 函数组件接收
function D(params) {
return (
<div className="pd20">
<h5>函数子组件</h5>
<h5>
从祖先组件获取值:
<Consumer>
{(value) => {
// return `${value.name},${value.age}`
return <span>{value.name},{value.age}</span>
}}
</Consumer>
</h5>
</div>
)
}
export default A

组件的问题优化
问题:只要执行setState(),即使不改变状态数据,组件也会重新render()
问题:只要当前组件重新render(),子组件就会重新render,导致效率低
-
解决方法:应该只有当组件的state或者props数据发生改变的时候才重新render
-
方法1
javascript
shouldComponentUpdate(nextProps, nextState) {
console.log(this.props, '目前组件实例自身上的props)')
console.log(this.state, '目前组件实例自身上的this.state')
console.log(nextProps, '下一次组件实例自身上的props)')
console.log(nextState, '下一次组件实例自身上的this.state')
// 应该只有当组件的state或者props数据发生改变的时候才重新render,比较繁琐
return !this.state.count === nextState.count
}
- 方法2,采用PureComponent
- PureComponent里面,只是重写了shouldComponentUpdate,比较纯粹的比较
- 有缺点,修改 数据的时候,如果赋值的对象,类似push和unshift等,不会改变数据
- 仅支持浅比较,复杂数据类型易失效
- 若 props/state 是复杂类型(对象、数组),即使内部值变化但引用地址不变,浅比较会判定为 "无变化",导致组件不更新(不符合预期)
javascript
import React, { Component, PureComponent } from 'react'
import './index.css'
//因为通过阀门控制的比较繁琐,所以新方法是不用Component,而是用PureComponent
class Parents extends PureComponent {
state = { count: 1 }
changeCount = () => {
const { count } = this.state
this.setState({
count: count + 1,
})
}
// 接收两个参数,下一个props状态,下一个state状态
// shouldComponentUpdate(nextProps, nextState) {
// console.log(this.props, '目前组件实例自身上的props)')
// console.log(this.state, '目前组件实例自身上的this.state')
// console.log(nextProps, '下一次组件实例自身上的props)')
// console.log(nextState, '下一次组件实例自身上的this.state')
// // 应该只有当组件的state或者props数据发生改变的时候才重新render,比较繁琐
// return !this.state.count === nextState.count
// }
render() {
console.log('父组件渲染')
const { count } = this.state
return (
<div className="pd20">
<h4>父组件</h4>
<h4>重复render的问题解决</h4>
<h6>数值:{count}</h6>
<button onClick={this.changeCount}>加</button>
<Son count={count} />
</div>
)
}
}
class Son extends Component {
render() {
console.log('子组件渲染')
return (
<div className="pd20">
<h4>子组件</h4>
<h4>子组件接收的值:{this.props.count}</h4>
</div>
)
}
}
export default Parents

renderProps
- 也叫插槽,
- 使用children props:通过组件标签传入结构
- 使用reder props:通过标签属性传入结构,一般用render函数属性
使用children props传入结构后
- 当我想要a组件里的值传给b组件
- 发现无法实现

javascript
import React, { Component, PureComponent } from 'react'
import './index.css'
class Parents extends Component {
render() {
return (
<div className="pd20">
<h4>父组件</h4>
<h4>renderProps</h4>
<A>
{/* 标签体内容 */}
111111
<B></B>
</A>
</div>
)
}
}
class A extends Component {
state = { name: '乞力马扎罗' }
render() {
return (
<div className="pd20">
<h4>子组件</h4>
{/* 要想渲染标签体内容,必须这样调用 */}
{/* 这里想给B组件传name,发现无法实现 */}
{this.props.children}
</div>
)
}
}
class B extends Component {
render() {
return (
<div className="pd20">
<h4>孙子组件</h4>
</div>
)
}
}
export default Parents
- 解决方法,通过renderProps传递B组件
- 优点,你只管留位置,下回想换组件,我直接在调用方修改
javascript
import React, { Component } from 'react'
import './index.css'
class Parents extends Component {
render() {
return (
<div className="pd20">
<h4>父组件</h4>
<h4>renderProps</h4>
{/* 给A传递的不是一般的props,是renderProps,这里可以传B,可以传任何你想渲染的组件 */}
<A render={(name) => <B name={name} />}></A>
</div>
)
}
}
class A extends Component {
state = { name: '乞力马扎罗' }
render() {
const { name } = this.state
return (
<div className="pd20">
<h4>A组件</h4>
{/* 这里相当于预留位置,具体预留给谁,看上面render谁,就留给谁,介个就是插槽 */}
{this.props.render(name)}
</div>
)
}
}
class B extends Component {
render() {
console.log(this.props, 'A组件传的值')
const { name } = this.props
return (
<div className="pd20">
<h4>B组件</h4>
<h4>A组件通过renderProps传过来的值:{name}</h4>
</div>
)
}
}
export default Parents

错误边界(备用页面)
- 用来捕获后代组件错误,渲染出备用页面
- 特点,只能捕获后代组件生命周期产生的错误,不能捕获自己组件中产生的错误和其它组件在合成事件,定时器中产生的错误

父组件
javascript
import React, { Component } from 'react'
import Children from './children'
import './index.css'
export default class Parents extends Component {
state = {
hasError: '', //用于标识子组件是否产生错误
}
// 这个生命周期的设计目的就是通过返回一个对象来更新组件的 state,只有键名和 state 中的属性一致,就会修改
// Parents的子组件出现报错会触发它的调用并携带错误信息
static getDerivedStateFromError(error) {
return {
hasError: error,
}
}
// 子组件(包括嵌套子组件)在 render 阶段、生命周期方法中抛出未捕获的错误时触发
componentDidCatch(error,info) {
console.log('这里可以统计错误次数',error,info)
}
render() {
return (
<div className="pd20">
<h4>父组件</h4>
{this.state.hasError ? (
<h2>当前网络不稳定,请稍后重试</h2>
) : (
<Children />
)}
</div>
)
}
}
子组件
javascript
import React, { Component } from 'react'
export default class Child extends Component {
state = {
// users: [
// {
// name: '乞力马扎罗',
// age: 18,
// },
// ],
users: '999',
}
render() {
return (
<div className="pd20">
<h4>子组件</h4>
{this.state.users.map((item) => {
return (
<div key={item.name}>
<h4>{item.name}</h4>
</div>
)
})}
</div>
)
}
}