1、useState:使函数组件可以使用并修改 state
import { useState } from 'react'
const [num0, setNum0] = useState(10)
const [num0, setNum0] = useState(()=>{return 10})// 复杂初始化
setNum(1)
setNum(pre => {return pre + 1}) // 类组件的setState
-
与类组件的
setState()
不同,不支持部分修改;所以推荐每个变量对应一个useState
; -
18中都是异步,多次fun会统一渲染;
-
16版本中,也与setState一致,多次执行时,在合成事件、周期函数中是异步渲染(react处理),在其他异步操作中:如定时器、手动事件绑定等没有react干预是依次同步渲染;
-
特别地,自带优化,当修改的值与之前的值一样时(Object.is),不重新渲染;
-
如何实现循环渲染:fun的传参:
for (let i = 0; i < 10; i++) {
setNum(pre => {
return pre + 1
})
}
// 类组件中setState循环
state = {
num: 0,
}
handle = () => {
for (let i = 0; i < 10; i++) {
this.setState(pre => {
return { num: pre.num + 1 }
})
}
}
2、useReducer:useState的升级版
- 一般用于组件内状态比较多时,一般useState即可
// 普通组件A.jsx
import React, { useReducer } from 'react'
const initState = {
num: 0,
}
const reducer = function (state, action) {
state = { ...state }
switch (action.type) {
case 'plus':
state.num++
break
case 'minus':
state.num--
break
}
return state
}
const A = () => {
let [state, dispatch] = useReducer(reducer, initState)
return (
<div>
<div>{state.num}</div>
<button onClick={() => {dispatch({ type: 'plus' })}}>增加</button>
<button onClick={() => {dispatch({ type: 'minus' })}}>减少</button>
</div>
)
}
export default A
3、useEffect:使函数组件可以使用 生命周期
import { useState, useEffect } from 'react'
useEffect(() => {})// componentDidMount、componentDidUpdate
useEffect(() => {},[])// componentDidMount
useEffect(() => {},[num])// componentDidMount、num更新时相当于下一次渲染的更新后
useEffect(() => { return ()=>{}})// 上一次销毁前
useEffect(() => { return ()=>{}},[num]) // 更新num后,触发上一次销毁前
useLayoutEffect() // 同上,内部有状态更新时,会在浏览器绘制前更新组件,阻止本次页面绘制
- 多个useEffect存放在
effect链表
中,等待视图挂载后依次执行;
4、useLayoutEffect
- 用法与
useEffect
一样,都能获取到真实DOM对象; - 区别:比
useEffect
早一步执行,是在root.render()
将虚拟dom解析出来真实dom对象后执行,此时浏览器还没有绘制, useEffect
:异步操作,与浏览器绘制渲染同时进行,官方更推荐使用;useLayoutEffect
:同步操作,在浏览器绘制渲染之前进行,内部如果更改状态,会阻止本次浏览器绘制,先去重新渲染组件再统一绘制;
5、useRef:使函数组件可以使用 ref
- ref:用于获取标签元素、组件实例
- 模仿类组件的两种方式:
// 函数方式
let box
useEffect(() => {
console.log(box)
}, [])
return (
<div ref={x => (box = x)}>
// React.createRef()方式
let box = React.createRef()
useEffect(() => {
console.log(box.current)
}, [])
return (
<div ref={box}>
- 等价于
React.createRef()
方式
let box = useRef(null)
useEffect(() => {
console.log(box.current)
}, [])
return (
<div ref={box}>
-
为什么还需要
useRef
:每次更新组件,拿到的ref对象都是原来的,而
React.createRef()
每次执行都重新创建,这一点对类组件没影响,因为类组件每次渲染是执行的render()
函数,不会重新执行React.createRef()
;
6、useImperativeHandle:获取hooks子组件状态
- 获取hooks组件内状态
const Demo = React.forwardRef((props, ref) => {
const [num, setNum] = useState(0);
useImperativeHandle(ref, () => {
return { num }// 这个对象将替掉返回的标签:{num:0}或{current:{num:0}}
})
- antd组件库中自有的获取form元素方式
// let form = useRef(null)
let [formIns] = Form.useForm()
----
<Form
// ref={form} // form.current
form={formIns} //直接:formIns
7、useMemo:性能优化hook1–计算缓存
let num = useMemo(()=>{return 2*y}) //不起缓存效果,每次组件更新都重新计算
let num = useMemo(()=>{return 2*y},[]) //一直使用缓存
let num = useMemo(()=>{return 2*y},[x,y,...]) //正确用法:状态x或y改变则重新计算
8、useCallback:性能优化hook2–方法缓存
-
一般用于防止子组件做不必要的更新;
-
子组件接收到这个num时,可以使用以下两种方式阻止自身做不必要的渲染:
- 类子组件:继承
class Demo extends PureComponent {render(){}}
- hooks子组件:使用
const Demo = React.memo(()=>{return ()})
- 类子组件:继承
const num = useCallback(()=>{}) //不起缓存效果,每次组件更新都重新创建函数
const num = useCallback(()=>{},[]) //一直使用缓存,内部变量始终为首次初始
const num = useCallback(()=>{},[y]) //正确用法:状态y改变则重新生成函数
9、自定义hooks:封装一些公共逻辑
- 第一步:函数,
use
命名 - 第二步:借助官方hooks,补充自定义逻辑;