加入依赖是在做什么
useEffect本身是组件每次渲染都会执行的,加入effect是为了减少其执行的次数
加入空数组代表只执行一次
加入其他变量,表示在其改变的时候执行
当你调用 useEffect时,就是在告诉 React 在完成对 DOM 的更改后运行你的“副作用”函数。
useEffect是在dom更新完成后做的事情。
一般来说,函数退出后,变量会销毁,而state中的变量会被react保留
const [count, setCount] = useState(0);
我们声明了一个叫count的state变量,然后把它设置为0。React会在重新渲染时记住它当前的值,并提供最新的值给我们的函数。
之所以叫做useState而不是createState,就是我们并不是每次都create这个state变量,而是只在组件第一次渲染的时候创建。
当用户点击后,我们传递一个新的值给setCount。React会重新渲染Example组件,并把最新的count传给它。
如果我们想使用多个state变量,它允许我们给不同的state变量取不同的名称。
我们可以单独更新每一个state。(理解: 我们单独更新一个state, react会重新渲染函数组件,并用每个state的最新值去渲染,我们更新的那个state状态,得到更新,而那些没有setState的变量,之前的值就是最新的)
当我们的state变量是一个复杂的对象或者数组时,不同于class的this.setState,更新state变量总是替换而不是合并。(理解: clss组件中的setState是合并式的更新,因此我们得以单独的跟新state中的某一个值)
副作用
数据获取、设置订阅以及手动更改react组件中的dom都属于副作用。
1、无需清除的副作用
有时候,我们只想在react更新dom之后运行一些额外的代码。比如发送网络请求
,手动更新dom,记录日志等。
在class组件中,render函数是不应该有任何副作用的。
我们基本都希望在react更新dom之后才执行我们的操作。
这就是为什么我们把副作用操作放在componentDidMount 和
componentDidUpdate函数中。
useEffect(() => {
document.title = you clicked ${count} times
})
通过这个hook,你可以告诉react组件需要在渲染后执行某些操作。
React会保存你传递的函数(我们将它称之为effect,重点理解:effect是我们
传递的函数!!!而不是useEffect这个hooks api),并且在执行dom更新
之后调用它。(理解:在useEffect中我们给它传递的是一个回调函数,将我们
需要的操作放在回调函数中,react会在每次更改dom后执行我们的回调函数)
理解: 因为我们传递的是一个函数,而函数是一个引用数据类型的数组,
所以传递给useEffect的函数在每次渲染中都会不一样,每次渲染都会有自己的
回调函数(内存地址不同,不是同一个函数对象),这是刻意为之。这正是
我们可以在effect中获取最新的count的值,而不用担心其过期的原因。
每次我们重新渲染,都会生成新的effect,替换掉之前的(effect是我们写在useEffect中的回调函数!!)
某种意义上讲effect更像是渲染结果的一部分——每个effect 属于一次特定的渲染。
不理解的地方:提示
与 componentDidMount 或 componentDidUpdate 不同,使用 useEffect 调度的 effect 不会阻塞浏览器更新屏幕,这让你的应用看起来响应更快。大多数情况下,effect 不需要同步地执行。在个别情况下(例如测量布局),有单独的 useLayoutEffect Hook 供你使用,其 API 与 useEffect 相同。
是什么意思?
解惑: useLayoutEffect 的函数签名与useEffect相同,但是它会在所有的DOM变更之后同步调用effect。可以使用它来读取DOM布局并同步触发重渲染。在浏览器执行绘制之前,
useLayoutEffect内部的更新计划将被同步刷新。尽可能的使用标准的useEffect以避免阻塞视觉更新。
useLayoutEffect与componentDidMount componentDidUpdate 的调用阶段是一样的。
理解: 就是说类似vue中的nextTick componentDidMount componentDidUpdate useLayoutEffect, 会在dom更新完毕,浏览器执行绘制(肉眼看到新的页面)之前执行,如果在这里面做复杂的计算就会阻塞页面的渲染。而useEffect会在页面渲染完毕之后执行,所以不会阻塞页面的绘制渲染。
http://www.soolco.com/post/73226_1_1.html。 // 这篇文章说的很清楚
2、需要清除的effect
如果你的effect返回一个函数,react将会在执行清除操作时调用它(理解:写在这里的回调函数会在react卸载的时候执行,你可以在这块空间做任何自己的操作,同样我们也是return一个函数定义给react,react会在销毁的时机去调用它)
忘记正确地处理componentDidUpdate是React应用中常见的bug来源。
useEffect默认就会处理,它会在调用一个新的effect之前对前一个effect进行清理。
如果某些特定值在两次重新渲染之间没有发生变化,可以通知react跳过对effect的调用,只要传递数组作为useEffect的第二个参数即可。理解:所以我们传递第二个参数是为了减少effecta执行的次数。
自定义hook是一个函数,函数内部可以调用其他的hook
与React组件不同的是,自定义hook不需要具有特殊的标识。我们可以自由的决定它的参数是什么,以及它应该返回的是什么(理解: 自定义hook不必须返回 一个值和一个set值的函数,换句话说,不非要成对出现,可以自由定义返回的东西,根据需求来)
在多个hook之间传递信息:
由于hook本身是函数,因此我们可以在他们之间传递信息。就像下面这样:
const [recipentID, setRecipentID] = useState(1);
const isRecipentOnline = useFriendStatus(recipientID);
当我们通过select调用setRecipentID ,改变了recipentID后,useFriendStatus也会用最新的recipientID来执行。
惰性初始state
initialState参数只会在组件的初始化渲染中起作用,后续渲染时会被忽略。如果初始state需要通过复杂计算获得,则可以传入一个函数,在函数中计算并返回初始的state,此函数只在初始渲染的时候被调用:
const [state, setState] = useState(() => {
const initialState = someExpensiveComputation(props);
return initialState
})
调用state hook的更新函数并传入当前的state时,react会跳过子组件的渲染以及effect的执行,react使用Object.is比较算法来比较state.
useCallback
const memoizedCallback = useCallback(() => {
doSomething(a, b);
}, [a, b]),
返回一个memoized的回调函数。
把内联回调函数及依赖项数组作为参数传入useCallback,
它将返回该回调函数的memoized版本,该回调函数仅仅在
某个依赖项改变时才会更新。
当把回调函数传递给经过优化的并使用引用相等性去避免非必要
渲染的子组件时,它将非常有用。比如(shouldComponentUpdate)
useCallback(fn, deps)相当于useMemo(() => fn, deps).
useMemo
返回一个memoized值。
把创建函数和依赖项数组作为参数传入useMemo,它仅会在某个依赖改
变时才会重新计算memoized值。这种优化有助于避免每次渲染时
都进行高开销的计算。
传入useMemo的函数会在渲染期间执行。不要在这个函数中执行与
渲染无关的操作。
可以把useMemo作为性能优化的手段,但不要把它作为语义上的保证。
应该先编写在没有useMemo的情况下也可以执行的代码—之后再在代码中
添加useMemo,以达到性能优化的目的。