**Q: **useEffect的执行顺序是什么样的?
A: useEffect在默认情况下,在第一次渲染和每次更新后都会执行,可以理解为useEffect发生在“渲染之后”,无需分辨“挂载后”还是“更新后”,React 保证了每次运行 effect 的同时,DOM 都已经更新完毕。
Q:useEffect第二个参数的作用
A: 这个参数接受一个数组,称之为依赖项,这个数组里的值包含了所有外部作用域中会随时间变化并且在 effect 中使用的变量。 在class component中,我们会通过componentDidUpdate来判断更新前后变量的值有没有发生变化从而执行相应的逻辑,hook中,如果依赖项中的值没有发生变化,React则会跳过useEffect的调用
**Q:**如何通过useEffect实现componentDidMount功能
A: 当我们传入一个空数组给第二个参数,意味着我们的effect函数中不依赖组件的任何变量,所以它永远都不需要重复执行,也就是只在组件第一次挂载后执行一次。
export function useDidMount(fn){
useEffect(fn,[])
}
**Q:**如何通过useEffect实现componentDidUpdate功能
**A:**同样利用第二个参数并结合useRef,结合代码注释
export function useDidUpdate(fn,condition){
const ref = useRef(false)//设置ref初始值为false,保证fn在组件更新后调用
useEffect(()=>{
if(ref.current){//此后保证组件更新后调用fn
fn()
}else{
ref.current = true//当组件第一次挂载完毕后,置ref为true,
}
},condition)
}
**Q:**如何添加全部依赖?
A: 靠自觉! 当然是不可能滴,官方给我们提供好了一个工具,lint规则,但工具有时候不是万能的,所以我们必须要了解我们正在写的代码,了解哪些值是可能变化的,
**Q:**在useEffect中调用函数后如何正确添加依赖?
A: 官方推荐在useEffect内部调用函数,这样函数中用到了哪些组件相关变量会更加清晰一些,
**Q:**如何在useEffect中调用外部函数?
A: 一些情况如函数中没有用到组件内变量的函数,我们可以把它放在函数组件外部,避免在依赖项中写入函数,
万不得已的情况下,你可以 把函数加入 effect 的依赖但 把它的定义包裹 进 useCallback Hook。这就确保了它不随渲染而改变,除非 它自身 的依赖发生了改变:
**Q:**如何包裹进useCallback?
A: 首先useCallback的作用是返回一个记忆化的函数,它将返回该回调函数的 memoized 版本,该回调函数仅在某个依赖项改变时才会更新,
useCallback(fn, deps) 相当于 useMemo(() => fn, deps)
所以:
const memoizedCallback = useCallback(
fn(a,b),
[a, b],
);
useEffect(()=>{
memoizedCallback()
},[memoizedCallback])
Q: 如果我的 effect 的依赖频繁变化,我该怎么办?
A: 这个通常发生于你在effect里做数据请求并且没有设置effect依赖参数的情况。没有设置依赖,effect会在每次渲染后执行一次,然后在effect中更新了状态引起渲染并再次触发effect。无限循环的发生也可能是因为你设置的依赖总是会改变。你可以通过一个一个移除的方式排查出哪个依赖导致了问题。但是,移除你使用的依赖(或者盲目地使用[])通常是一种错误的解决方式。你应该做的是解决问题的根源。举个例子,函数可能会导致这个问题,你可以把它们放到effect里,或者提到组件外面,或者用useCallback包一层。useMemo 可以做类似的事情以避免重复生成对象。
Q: 为什么有时候在effect里拿到的是旧的state或prop呢?
A: Effect拿到的总是定义它的那次渲染中的props和state。这能够避免一些bugs,但在一些场景中又会有些讨人嫌。对于这些场景,你可以明确地使用可变的ref保存一些值(上面文章的末尾解释了这一点)。如果你觉得在渲染中拿到了一些旧的props和state,且不是你想要的,你很可能遗漏了一些依赖。可以尝试使用这个lint 规则来训练你发现这些依赖。可能没过几天,这种能力会变得像是你的第二天性。