3.1 纯函数如何处理副作用 useEffect 基本使用
纯函数的概念:
-
只负责自己的任务,它不会更改在该函数调用前就已存在的对象或变量
-
输入相同,则输出相同。给定相同的输入,纯函数应总是返回相同的结果
副作用的概念:
-
函数在执行过程中对外部造成的影响称之为副作用,例如:Ajax调用、DOM操作、与外部系统同步等
-
在React组件中,事件操作是可以处理副作用的,但有时候需要初始化处理副作用,那么就需要useEffect钩子
useEffect 触发时机: JSX 渲染后触发的
import { useRef, useEffect } from "react"
export default function App() {
const ref = useRef(null)
// 副作用:不符合纯函数的规范
// setTimeout(() => {
// ref.current.focus()
// },3000)
const handleClick = () => {
// 副作用:符合纯函数的规范,因为事件是可以处理副作用的
ref.current.focus()
}
// 可以在初始的时候进行副作用操作
useEffect(() => {
ref.current.focus()
})
return (
<div>
hello App
<button onClick={handleClick}>点击</button>
<input type="text" ref={ref} />
</div>
)
}
port { useEffect, useState } from "react"
export default function App() {
const [count,setCount]=useState(0)
// 初始渲染和更新渲染,都会触发useEffect() -> 因为每次渲染JSX后,后会出发useEffect(),整个当前函数组件作用域的最后时机触发的
useEffect(() => {
console.log(count);
})
const handleClick = () => {
setCount(count+1)
}
return (
<div>
hello App
<button onClick={handleClick}>点击</button>{count}
</div>
)
}
3.2 分开处理副作用 useEffect 的依赖项的使用
可以通过将依赖项数组指定为调用的第二个参数来告诉 React 跳过不必要的重新运行 Effect
-
当依赖项为空数组的时候,只会初始触发,更新不会触发
-
ESLint 会检测依赖项是否正确,包括:props, state, 计算变量等
import { useEffect, useState } from "react"
export default function App() {
const [count, setCount] = useState(0)
const [msg, setMsg] = useState('hello React')
// 初始的时候,所有的useEffect都会触发
// 更新的时候,只有对应依赖项发生改变的才会触发
// 内部是通过 Object.is() 来判定是否改变
useEffect(() => {
console.log(count);
}, [count])
useEffect(() => {
console.log(msg);
}, [msg])
// 当为空数组的时候,只有初始化时会触发,更新的时候不会触发
useEffect(() => {
console.log(123);
}, [])
const handleClick = () => {
setCount(count+1)
}
return (
<div>
hello App
<button onClick={handleClick}>点击</button>
{count},{msg}
</div>
)
}
3.3 尽量在 useEffect 内定义函数
import { useEffect, useState } from "react"
export default function App() {
const [count, setCount] = useState(0)
useEffect(() => {
const foo = () => {
console.log(count);
}
foo()
},[count])
return (
<div>
hello App
</div>
)
}
3.4 useEffect 的清理操作的重要性
当卸载组件或更新组件的时候,可以通过useEffect来实现一些清理工作
ps:严格模式下,会检测useEffect是否实现了清理操作
初始化数据时,要注意清理操作,所以更简洁的方式是使用第三方,例如: ahooks 中的 useRequest
3.5 实验性的useEffectEvent
使用一个特殊的 Hook 调用 useEffectEvent 来从你的 Effect 中提取这个非反应性逻辑
实验版本: react@experimental、react-dom@experimental
3.6 useLayoutEffect 同步执行状态更新
-
useEffect() 实在渲染被绘制到屏幕之后执行的,是异步的;useLayoutEffect() 是在渲染之后但在屏幕更新之前,是同步的
-
大部分情况下我们采用 useEffect(),性能更好。但当你的 useEffect 里面的操作需要处理 DOM,并且会改变样式,就需要用 useLayoutEffect ,否则可能会出现闪屏问题
import { useEffect, useLayoutEffect, useRef } from "react" export default function App() { const ref = useRef(null) // useLayoutEffect 在 useEffect 之前触发 useEffect(() => { console.log(1, ref.current); }) useLayoutEffect(() => { console.log(2, ref.current); }) return ( <div> hello App <div ref={ref}>aaaaa</div> </div> ) }
3.7 useInsertionEffect DOM更新前触发
useInsertionEffect 应用场景非常少,因为获取不到 DOM 元素,所以只能在 CSS-in-JS 库中才会使用
import { useInsertionEffect, useRef } from "react";
function App() {
const ref = useRef(null)
useInsertionEffect(() => {
const style = document.createElement('style')
style.innerHTML = `
.box{
background:red;
width:100px;
height:100px;
}
`
document.head.appendChild(style)
})
return (
<div>
hello App
<div className="box" ref={ref}>aaaaa</div>
</div>
)
}
export default App