react笔记-扩展篇
包括新用法和hooks
1. setState
setState更新状态的两种写法:
1. setState({}, [() => {}])
2. setState(() => {
// 返回一个状态修改对象
return {}
}, [() => {}])
1. 对象写法:
⚠️注意:react更新状态是异步的
import React, { Component } from 'react'
export default class Demo extends Component {
state = {
count: 0
}
add = () => {
// 更新状态:react更新状态是异步的
this.setState({
count: this.state.count + 1
}, () => {
// 该回调的可以获取到state更新后的结果,可选参数
console.log(this.state.count); // 1 更改后
})
console.log(this.state.count); // 0 更改前
}
render() {
return (
<div>
<p>{this.state.count}</p>
<button onClick={this.add}>+1</button>
</div>
)
}
}
2. 函数写法:
回调参数可以收到state
和props
this.setState((state, props) => {
return {
count: state.count + 1
}
})
总结:
对象式的setState
是函数式的setState
方式的简写方式(语法糖)
2. lazyLoad
路由组件的lazyLoad:懒加载
import React, { Component, lazy, Suspense } from 'react'
import { Route } from 'react-router-dom'
const Home = lazy(() => import('./Home'))
export default class Demo extends Component {
render() {
return (
<div>
{/* Suspense 包裹路由组件, 需要给定fallback */}
<Suspense fallback={<div>loading...</div>}>
<Route path='/home' component={Home} />
</Suspense>
</div>
)
}
}
3. Hooks
3.1 Hooks是什么
hooks是react16.8.0新增的新特性,可以在函数式组件中使用state
以及其他react新特性。
3.2 三个常用的hook
-
State Hook
:React.useState() -
Effect Hook
:React.useEffect() -
Ref Hook
:React.useRef()
3.3 useState
State Hook让函数组件也可以拥有state状态,并进行状态数据的读写操作。
import React from 'react'
function Demo() {
const [count, setCount] = React.useState(0)
function change() {
// 第一种写法:参数为非函数值,直接指定新的状态值,内部用其覆盖原来的状态值
setCount(count + 1)
// 第二种写法:参数为函数,接收原来的状态值,返回新的状态值,内部用其覆盖原来的状态值
setCount((num) => {
return num + 1
})
}
return <>
<div>{count}</div>
<button onClick={change}>+1</button>
</>
}
export default Demo
通过import { useState } from 'react'
也可以
3.4 useEffect
import { useState, useEffect } from 'react'
function Demo() {
const [count, setCount] = useState(0)
// 情况1:只给第一个参数:每当数据更改一次,调用一次回调
useEffect(() => {
// 可以执行任何副作用操作
// 例如:网络请求,定时器
console.log('数据更改了');
})
// 情况2:如果第一个参数返回一个函数,返回的函数可以做一些收尾工作,例如:清除定时器
useEffect(() => {
console.log('数据更改了');
return () => {
console.log('组件销毁了');
}
})
// 情况3:监听count的改变,count改变了,就会调用回调
useEffect(() => {
console.log('数据更改了');
}, [count, ...])
// 情况4:参数2为空数组,谁都不监听,回调只有第一次render才会被调用
useEffect(() => {
console.log('数据更改了');
}, [])
return <>
<div>{count}</div>
<button onClick={() => { setCount(count + 1) }}>+1</button>
</>
}
export default Demo
可以把useEffect
看作三个函数的组合:
componentDidMount()
componentDidUpdate()
componentWillUnmount()
3.5 useRef
import { useRef } from 'react'
function Demo() {
let myRef = useRef()
function getRef() {
console.log(myRef.current.value)
}
return <>
<input type="text" ref={myRef} />
<button onClick={getRef}>ref</button>
</>
}
export default Demo
4. Fragment
类似于vue的template,不会解析到页面上
<Fragment></Fragment>
<></>
import { Fragment } from 'react'
function Demo() {
// Fragment可以绑定key,循环的时候可以传递key,空标签不允许传任何属性
return <Fragment key={id}>
hello
</Fragment>
}
export default Demo
5. Context
Context:一种组件间的通信方式。常用于【祖组件】与【后代组件】间的通信。
使用:
- 创建Context容器对象:
const MyContext = createContext()
- 渲染子组件时,外层包裹
XxxContext.Provider
,通过value
属性给后代组件传递数据
export default class A extends Component {
render() {
return (
<div>
A
<MyContext.Provider value={{ name: 'xiaotian', age: 18 }}>
<B />
</MyContext.Provider>
</div>
)
}
}
也可以解构Provider:
const { Provider } = MyContext;
<Provider></Provider>
-
后代组件读取数据(两种方式):
- 第一种方式:仅适用于类式组件
class C extends Component { // 声明接收context,通过this.context读取value的数据 static contextType = MyContext render() { return ( <div> C {this.context.name} </div> ) } }
- 第二种方式:函数式和类式组件都行
<XxxContext.Consumer> { (value) => { // value就是context中value的数据 return (需要返回的内容) } } </XxxContext.Consumer>
例子:
// C组件 function C() { return ( <MyContext.Consumer> { (value) => { return ( <div> C {value.name} </div> ) } } </MyContext.Consumer> ) }
6. 组件优化
6.1
import {Component} from 'react'
的Component的两个问题:- 只要执行
setState()
,即使不改变状态,组件也会重新render()
- 当前组件重新
render
,就会自动重新render
子组件(即使子组件没有用到父组件的任何数据),效率低
6.2 效率高的做法:
只有当组件的
state
和props
发生改变的时候才重新render
6.3 原因:
Component中的
shouldComponentUpdate()
总是返回true6.4 解决方法:
类式组件:
-
方法1:重写
shouldComponentUpdate()
方法,比较新旧state
和props
数据,只有发生变化才返回true,否则返回false -
方法2:使用
PureComponent
,PureComponent
重写了shouldComponentUpdate()
,只有当state
和props
发生变化时才返回true。import { PureComponent } from 'react'
⚠️注意:只是进行
state
和props
数据的浅比较,如果只是数据对象内部数据变了,返回的是false。不要直接修改state,而是要产生新数据
函数式组件:可以通过使用
React.memo
来实现类似PureComponent
的优化效果。下面是关于React.memo
的基本使用方法:-
基本用途:
React.memo
是一个高阶函数,它接收一个函数组件作为参数,并返回一个新的经过优化的组件,该组件在props没有变化时避免不必要的渲染。 - 用法示例:
import React, { memo } from 'react' const MyFunctionalComponent = (props) => { // 组件逻辑... return <div>{props.text}</div> } export default memo(MyFunctionalComponent)
在这个例子中,
MyFunctionalComponent
被memo
包裹,这意味着React会跳过重新渲染该组件,除非它的props发生了变化。-
深入理解:与
PureComponent
相似,React.memo
执行浅比较来检查props是否变化。如果props是复杂对象且需要深层比较,你可以传递第二个参数,即一个自定义的比较函数来进行更精细的控制。 -
注意事项:
React.memo
只对props的变化做出反应,组件内部的state或context变化依然会导致重渲染。此外,如果组件有副作用(如数据获取、订阅等),应该在组件内部使用适当的生命周期方法或Hooks来管理这些副作用。
通过这种方式,
React.memo
帮助提高了函数式组件的性能,减少了不必要的渲染,特别是在大型应用程序中,这种优化可以带来显著的性能提升。
7. renderProps(插槽技术)
类似于vue中的插槽
import React, { Component } from 'react'
export default class Demo extends Component {
render() {
return (
<div>
{/* <BComponent name={name} /> 可以渲染任何组件 */}
<AComponent render={(name) => <BComponent name={name} />} />
</div>
)
}
}
// 父组件
class AComponent extends Component {
render() {
return (
<div>
<div>A组件</div>
{/* 预留的位置,类似于vue中的插槽 */}
{ this.props.render('xiaotian') }
</div>
)
}
}
// 子组件
class BComponent extends Component {
render() {
return (
<div>B组件---A组件的name为:{this.props.name}</div>
)
}
}
8. 错误边界
8.1 理解:
错误边界(Errorboundary):用来捕获后代组件的错误,渲染出备用页面。
8.2 特点:
只能捕获后代组件生命周期产生的错误,不能捕获自己组件产生的错误和其他合成事件、定时器中产生的错误。
8.3 使用:
// 当子组件报错时,会触发getDerivedStateFromError,并携带错误信息
static getDerivedStateFromError(error) {
console.log(error);
return { hasError: true }
}
// 当渲染组件时出错会调用该钩子,用于统计错误信息,反馈给服务器
componentDidCatch(error, errorInfo) {
console.log(error, errorInfo);
}
import React, { Component } from 'react'
export default class Parent extends Component {
state = {
hasError: '' // 用于标识子组件是否产生错误
}
// 当子组件报错时,会触发getDerivedStateFromError,并携带错误信息
static getDerivedStateFromError(error) {
console.log(error);
return { hasError: error }
}
// 当渲染组件时出错会调用该钩子,用于统计错误信息,反馈给服务器
componentDidCatch(error, errorInfo) {
console.log(error, errorInfo);
}
render() {
return (
<div>
{ this.state.hasError ? '当前网络不稳定' : <Child /> }
</div>
)
}
}
class Child extends Component {
state = {
// list: [1, 2, 3, 4, 5] 正常情况
list: 'abc' // 错误情况
}
render() {
return (
<ul>
{
this.state.list.map((item, index) => {
return <li key={index}>{item}</li>
})
}
</ul>
)
}
}