现代 React Web 开发实战
宋一玮
FreeWheel 中国研发中心前端架构师
16115 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 31 讲
现代 React Web 开发实战
15
15
1.0x
00:00/00:00
登录|注册

10|React Hooks(下):用Hooks处理函数组件的副作用

你好,我是宋一玮,欢迎回到 React 应用开发的学习。
上节课我们讲了什么是 Hooks,React 18 里都有哪些 Hooks,然后深入学习了基础 Hooks 之一的 useState ,在结束前也介绍了 useRef
这节课我们紧接着来学习另一个基础 Hook:useEffect ,以及用于组件性能优化的 Hooks:useMemouseCallback 。讲完这些 Hooks,我们回过头了解一下所有 React Hooks 共通的使用规则。最后回答上节课一开始提到的疑问:
函数组件加 Hooks 可以完全替代类组件吗?
还有必要学习类组件吗?
好的,我们先从 useEffect 开始。

什么是副作用?

副作用(Side-effect,或简称 Effect)这个概念在上节课已经多次出现了,你可能还是觉得迷惑,到底什么是副作用?
计算机领域的副作用是指:
当调用函数时,除了返回可能的函数值之外,还对主调用函数产生附加的影响。例如修改全局变量,修改参数,向主调方的终端、管道输出字符或改变外部存储信息等。
总之,副作用就是让一个函数不再是纯函数的各类操作。注意,这个概念并不是贬义的,在 React 中,大量行为都可以被称作副作用,比如挂载、更新、卸载组件,事件处理,添加定时器,修改真实 DOM,请求远程数据,在 console 中打印调试信息,等等。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

React Hooks(下):用Hooks处理函数组件的副作用 本文深入探讨了React Hooks中的 `useEffect` 以及用于组件性能优化的Hooks:`useMemo` 和 `useCallback`。文章首先解释了副作用的概念,指出在计算机领域,副作用是指函数调用除了返回值外对主调函数产生的附加影响。在React中,诸如挂载、更新、卸载组件、事件处理、添加定时器、修改真实DOM、请求远程数据等行为都可以被称作副作用。此外,文章还提到了state绑定在组件函数之外的 `FiberNode` 上,从而引出了组件函数执行state更新函数也是一种副作用的观点。 通过深入讨论 `useEffect` 以及用于组件性能优化的Hooks:`useMemo` 和 `useCallback`,读者可以了解如何使用Hooks处理函数组件中的副作用,并学习如何优化组件性能。最后,文章回答了函数组件加Hooks是否可以完全替代类组件以及是否有必要学习类组件的疑问,为读者提供了全面的认识和思考角度。 本文通过深入讨论React Hooks中的 `useEffect` 以及用于组件性能优化的Hooks:`useMemo` 和 `useCallback`,帮助读者更好地理解和应用React Hooks,同时回答了相关疑问,为读者提供了全面的学习和思考视角。 文章还介绍了`useMemo` 和 `useCallback` 的完整概念和最典型的使用场景。`useMemo` 的功能是为工厂函数返回一个记忆化的计算值,在两次渲染之间,只有依赖值数组中的依赖值有变化时,该Hook才会调用工厂函数重新计算,将新的返回值记忆化并返回给组件。而`useCallback` 则会把作为第一个参数的回调函数返回给组件,只要第二个参数依赖值数组的依赖项不改变,它就会保证一直返回同一个回调函数(引用),而不是新建一个函数,从而优化组件性能。 此外,文章还强调了Hooks的使用规则,包括只能在React的函数组件中调用Hooks以及只能在组件函数的最顶层调用Hooks等限制,以及解释了这些限制的原因。 总之,本文内容丰富,深入探讨了React Hooks中的 `useEffect` 以及用于组件性能优化的Hooks:`useMemo` 和 `useCallback`,并介绍了Hooks的使用规则,为读者提供了全面的学习和思考视角。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《现代 React Web 开发实战》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(9)

  • 最新
  • 精选
  • 🐑
    置顶
    你好,我是《现代React Web开发实战》的编辑辰洋,这是👇项目的源代码链接,供你学习与参考:https://gitee.com/evisong/geektime-column-oh-my-kanban/releases/tag/v0.10.0
    2022-09-19归属地:北京
    1
  • 船长
    不是很理解 useMemo 那个例子,比如用 下面这个useEffect 写法不是也可以持久化记忆数据吗?好像只是比 useMemo 那种写法多了个 setXXX 所造成的一次渲染? const [num, setNum] = useState("0"); const [num2, setNum2] = useState("0"); useEffect(() => { const n = parseInt(num, 10); setNum2(fibonacci(n)); }, [num]);

    作者回复: 你好,船长,从实现功能来讲你用useState+useEffect代替useMemo是可行的。正如你说的,确实多了一次渲染。具体来说,useMemo的回调是在渲染过程中执行,而useEffect的回调会在提交阶段执行,useEffect回调内部调用了useState的话,会触发一次新的渲染。从用户感知上应该没太大差别,毕竟渲染过程是异步的,useEffect的回调也是异步的。 在实际项目中,我们可能会遇到连续在一个组件中写十来个Hooks的情况,每当存在这种跨多次渲染还相关的Hooks,都会多少增加一些调试的难度。

    2022-09-21归属地:北京
    2
    2
  • 学习前端-react
    请问:use Effect 的执行是可以拿到真实dom的,那为啥在图中提交阶段却是在真实dom之前?

    作者回复: 你好,学习前端-react,抱歉这几天在赶稿回复晚了。 你说得对。useEffect的副作用回调和清除函数,它们都在提交阶段执行,且大多情况都是异步执行的。这些函数被加入callback更新队列。在真实DOM绘制之后,下一次React渲染之前,这个队列里的callback会依次执行,所以这时候callback能拿到真实DOM。 这张图在第一次画时,只画了同步执行的情况,给大家造成了误解,还请见谅。目前图片已经做了更新,依然省略了一些实现细节,但更贴近实际情况了,请以这一版为准。

    2022-09-14归属地:北京
    3
    2
  • 都市夜归人
    const KanbanCard = ({ title, status }) => { const [displayTime, setDisplayTime] = useState(status); useEffect(() => { const updateDisplayTime = () => { const timePassed = new Date() - new Date(status); let relativeTime = '刚刚'; // ...省略 setDisplayTime(relativeTime); }; const intervalId = setInterval(updateDisplayTime, UPDATE_INTERVAL); updateDisplayTime(); return function cleanup() { clearInterval(intervalId); }; }, [status]); 可以看到,useEffect 接收了副作用回调函数和依赖值数组两个参数,其中副作用回调函数的返回值也是一个函数,这个返回的函数叫做清除函数。组件在下一次提交阶段执行同一个副作用回调函数之前,或者是组件即将被卸载之前,会调用这个清除函数。 没有看懂,上面的哪有两个参数啊?

    作者回复: 你好,都市夜归人,抱歉这两周在赶稿回复晚了。这里说的是useEffect的两个参数。 const KanbanCard = ({ title, status }) => { const [displayTime, setDisplayTime] = useState(status); 这段代码只是为了提示一下useEffect所在的相对位置。 下面的: useEffect(() => { // ... return function cleanup() { // ... }; }, [status]); 中的: () => { // ... return function cleanup() { // ... }; } 就是useEffect第一个参数,称作副作用回调函数(Effect Callback); 而后面的: [status] 就是第二个参数,称作依赖值数组(Dependencies)。

    2022-09-15归属地:北京
    4
    1
  • 满眼星🌟 辰🍊
    图例中,useLayoutEffect是同步更新dom,应该在useEffect之前执行,不是吗

    作者回复: 你好,满眼星辰,抱歉这几天在赶稿回复晚了。 你说得对。useEffect记录的副作用回调和清除函数,都在提交阶段执行,且大多情况都是异步执行的。这些函数被加入callback更新队列。在真实DOM绘制之后,下一次React渲染之前,队列里的callback会依次执行。 在第一次画图例时,只画了同步执行的情况,给你造成了误解,还请见谅。目前图片已经做了更新,依然省略了一些实现细节,但更贴近实际情况了,请以这一版为准。

    2022-09-16归属地:北京
  • 都市夜归人
    其实这两个 Hooks 与 useEffect 并不沾亲带故。且不说它们的用途完全不同,单从回调函数的执行阶段来看,前者是在渲染阶段执行,而后者是在提交阶段。 这句话与上面的生命周期图不太一致,求解惑

    作者回复: 你好,都市夜归人,这两周我都在赶稿,抱歉回复晚了。这里所指的“回调函数”,对于: const memoized = useMemo(() => createByHeavyComputing(a, b), [a, b]); 指的是其中的() => createByHeavyComputing(a, b),同时也叫工厂函数; 对于 const memoizedFunc = useCallback(() => {if (a) b()}, [a, b]); 指的是() => {if (a) b()}; () => createByHeavyComputing(a, b) 和 () => {if (a) b()} 如果符合执行条件的话,会在渲染阶段执行。 对于 useEffect(() => { if (province === '山东') { setCities(['济南', '青岛', '淄博']); }}, [province]); 指的是 () => { if (province === '山东') { setCities(['济南', '青岛', '淄博']); }} 如果符合执行条件的话,() => { if (province === '山东') { setCities(['济南', '青岛', '淄博']); }} 会在提交阶段执行。

    2022-09-16归属地:北京
    2
  • Imart
    useEffect 是在浏览器渲染/呈现dom内容后执行的; useLayoutEffect 是在真实dom更新后,浏览器渲染dom内容前执行的,即在render函数执行后,接着同步马上执行回调函数内容;
    2022-11-15归属地:广东
    2
  • Socrakit
    我试图在 handleSubmit 里调用 handleSaveAll(),希望每次新增“待处理” 后自动保存,但失败了。调用 handleSaveAll 的时候 todoList 还没有被更新,第二次新增后才会把第一次新增的保存进去,这是为什么呢 ? const handleSubmit = (title) => { const d = new Date() const n = d.toLocaleDateString().replace('/', '-') + ' ' + d.toLocaleTimeString(); setTodoList(currentTodoList => [ { title, dt: n}, ...currentTodoList ]); handleSaveAll() };
    2023-07-12归属地:江苏
  • Geeker
    个人觉得框架不应该把“负担” 甩给用户
    2022-11-04归属地:浙江
收起评论
显示
设置
留言
9
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部