[React05|青训营笔记]

92 阅读3分钟

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正在加载中