3.5 useEffect
//通过使用这个hook 告诉react组价需要在渲染后执行某些操作
useEffect(callback,array)
粗浅理解:把生命周期的DidMount和DidUpdated做了一个简单的合并
不同点:
使用useEffect调度的effect不会阻塞浏览器更新屏幕,让应用看起来响应的更快 ——React官网
每次渲染后都会执行:在组件初次渲染时会执行,在组件完成更新时也会执行。
为什么在组件内部调用:可以直接访问count state变量
执行时间点:在真实DOM构建完成之后
执行方式:异步
如果你的effect返回一个函数,React将在执行清除操作时调用它
-----React官网
清理函数执行时机:在每一次运行副作用函数之前运行
1.render+useEffect
2.render+清理函数(杀死上一个useEffect)+useEffect
3.render+清理函数(useEffect)+useEffect
4.循环
副作用:纯函数在引用外部变量或调用外部函数时
在进行数据请求,检测数据更新,和垃圾回收时比较常用
用来检测数据更新时
想监测哪一个更新就把这个变量写在数组中
当要检测页面中所有变量时 可以把所有变量都填写到数组中 也可以直接不写数组 即第二个参数
当不想检测页面中任何数据时 直接写一个空数组 即[ ]
只要不是在组件渲染时用到的变量,所有的操作都是副作用
使用场景:
- 修改DOM
- 修改全局变量
- ajax请求
- 计时器
- 存储相关
即和外部变量的交互都需要用到副作用
3.51 useEffect&useLayoutEffect
差异:
- useEffect是异步执行的,而useLayoutEffect是同步执行的
- useEffect执行时机是浏览器完成渲染之后,而useLayoutEffect执行时机是浏览器把内容真正渲染到界面之前同步执行,和componentDidMount等价
一个例子
import React, { useEffect, useLayoutEffect, useState } from 'react';
import logo from './logo.svg';
import './App.css';
export default function App() {
const [state, setState] = useState("hello world")
useEffect(() => {
let i = 0;
while(i <= 100000000) {
i++;
};
setState("world hello");
}, []);
// useLayoutEffect(() => {
// let i = 0;
// while(i <= 100000000) {
// i++;
// };
// setState("world hello");
// }, []);
return (
<>
<div>{state}</div>
</>
);
}
如果是用useEffect,点击刷新后会先闪烁一下helloworld然后再变成helloworld,而换成useLayoutEffect之后闪烁现象就会消失
所以最好把操作dom相关的操作放到useLayoutEffect中去,避免导致闪烁。
- 优先使用useEffect,因为它是异步执行的,不会阻塞渲染
- 会影响渲染的操作尽量放到useLayoutEffect中去,避免闪烁问题
- useLayoutEffect和componentDidMount是等价的,会同步调用,阻塞渲染
3.6useContext&createContext
绕开父组件,直接从顶级组件传到子组件,实现跨级组件传值
方法一:使用useContext来调用上下文
// 1、创建上下文
const NumContext = createContext();
// 子组件
function Count(){
// 3、绕过父组件调用上下文内容
const num = useContext(NumContext)
return (
<h3>{num}</h3>
)
}
function Father(){
return <Count/>
}
//顶级组件
export default function App(){
const [num,setNum]=useState(0)
return(
<NumContext.Provider value={num}>
<Father/>
</NumContext.Provider>
)
}
方法二:使用Provider与Consumer
function Child() {
return(
<NumContext.Consumer>
{//用花括号是因为要结构出num和setNum
({num,setNum})=>
(
<>
<h2>{num}</h2>
<button onClick={()=>setNum()}>修改num</button>
</>
)
}
</NumContext.Consumer>
)
}
export default function App() {
const [num,setNum]=useState(123)
//提供给子组件用来修改num的函数
//直接放在共享空间里面
return(
<NumContext.Provider value={{num,setNum}}>
<Father />
</NumContext.Provider>
)
3.7 use Transition
默认情况下,认为React中所有的更新都是紧急的,也就是说所有优先级相同,这样的话会导致一个问题:快速更新被大量更新拖慢速度
从React18中新增特性concurrency之后,可以将某些更新标记为可中断的和非紧急的,也就是所谓的transitions,将紧急任务的更新和非紧急任务的渲染区分开了。
const [isPending,startTransition]=useTransition();
返回一个具有两个成员的数组:
isPending:指明这个transition正在加载中