"与事件处理程序不同,事件处理程序只在每次交互时运行一次,而 Effect 则在需要进行同步时运行。"但是 交互往往会同时触发事件处理,从而引起值变化,进而导致同步,从而运行Effect,不是吗?那么如何确定方法应该写在事件处理里还是Effect里面??

"与事件处理程序不同,事件处理程序只在每次交互时运行一次,而 Effect 则在需要进行同步时运行。"
但是 交互往往会同时触发事件处理,从而引起值变化,进而导致同步,从而运行Effect,不是吗?
那么如何确定方法应该写在事件处理里还是Effect里面??

事件处理程序(Event Handler)和React中的Effect(如useEffect Hook)确实都在响应用户交互或状态变化时执行,但它们的作用和执行时机有所不同:

  1. 事件处理程序

    • 事件处理程序在特定的DOM事件触发时运行一次,例如点击按钮、输入框内容改变等。
    • 它们通常用来直接处理用户交互,更新应用的状态或者进行即时反馈。
  2. React Effect(以useEffect为例)

    • useEffect会在组件渲染后以及其依赖项发生变化时执行。它用于执行那些与UI渲染结果有间接关联的副作用操作,比如订阅数据源、设置定时器、手动更改DOM、添加或移除事件监听器等。
    • Effect可以指定一个依赖数组,只有当数组中声明的依赖项发生改变时,才会重新执行Effect。

确定方法应该写在事件处理还是Effect里面的关键在于理解任务的性质:

  • 如果任务是直接响应某个特定用户交互并立即更新UI状态,那么它应该是一个事件处理程序。
  • 如果任务是基于组件内部状态的变化而需要进行某种清理工作,或者对外部环境(如浏览器API、全局状态管理库等)有副作用的操作,并且这些操作不直接影响当前渲染的结果,那么该任务更适合放在useEffect中。

举个例子:

// 在事件处理程序中更新状态 const [count, setCount] = useState(0); function handleClick() { // 直接响应用户点击事件,更新计数 setCount(count + 1); } return <button onClick={handleClick}>Click me</button>;
// 在useEffect中进行副作用操作 const [text, setText] = useState(''); useEffect(() => { // 当text状态变化时,使用浏览器API自动聚焦到文本框 document.getElementById('myInput').focus(); // 清理:当组件卸载时,取消已设置的焦点 return () => { document.getElementById('myInput').blur(); }; }, [text]); return <input id="myInput" value={text} onChange={(e) => setText(e.target.value)} />;

在这个例子中,增加计数的操作是在事件处理程序中完成的,因为它直接响应用户的点击事件。而聚焦文本框的操作则是在useEffect中实现的,因为它不是直接的用户交互反馈,而是对内部状态变化的一种间接反应,并且涉及到了浏览器DOM API的调用。

比如 点击切换下拉,导致聊天室切换,断开重连,这个场景中,下拉框切换导致的直接影响是下拉框的值变化,而聊天室的切换(断开重连),是由于下拉框的值变化间接影响导致的。
也就是说,一个交互导致了A值的变化,而A的变化又导致了B的变化。对于A的变化,应放在事件处理程序中完成,对于B的变化,应该放在Effect中进行。

又比如,点击切换是否允许移动的复选框,直接导致的是复选框的值变化,而复选框的值一旦变化,会间接导致鼠标移动,图形是否跟随移动。
因此,复选框的值变化,应该放在事件处理程序中完成,而由于复选框的值变化导致的图形是否跟随鼠标移动的这个间接反映,应该放到Effect中。

比如有一个是否加密的复选框,勾选加密后,聊天室应该加密连接。勾选加密,导致的是复选框的值变化,间接导致聊天室的连接方式的变化,
因此加解密的状态值变化应该放在事件处理程序中,切换聊天室加解密连接的反应,应放在Effect中。

注意:Effect的依赖性不应该是对象或者函数!!!

如何理解 Effect Api?
在React中,Effect API主要指的是useEffect Hook。它允许你在函数组件内部执行副作用操作,这些操作可能包括但不限于:

  1. 订阅:监听某个数据源的变化(例如,订阅WebSocket、监听浏览器事件等)。
  2. 异步操作:发起HTTP请求、定时任务或其他异步逻辑。
  3. 手动更改DOM:直接操作真实DOM节点(尽管大多数情况下应尽量避免,而使用React状态来驱动视图更新)。
  4. 清理工作:当组件卸载时,取消已设置的订阅、清除定时器或释放资源。

理解useEffect的一个重要点在于它的执行时机和依赖关系管理:

  • 执行时机
    • 默认情况下,useEffect会在每次组件渲染后执行一次。
    • 如果给useEffect传递第二个参数(一个依赖数组),那么只有当数组中的依赖项发生变化时,该Effect才会重新执行。
useEffect(() => { // 这里的代码会在组件渲染后运行 // ... // 清理函数 return () => { // 组件卸载前执行的清理逻辑 // ... }; }, [dependency1, dependency2]); // 当dependency1或dependency2变化时,再次执行
  • 生命周期对应
    • 在类组件中,useEffect可以替代componentDidMountcomponentDidUpdate以及componentWillUnmount这三个生命周期方法的功能,根据传入的依赖数组进行相应的优化。

总之,React的useEffect API是处理组件间副作用的一种机制,它能够确保你的副作用代码符合React的理念,即响应式地根据组件状态的变化而触发,并且支持优雅地清理资源。通过合理利用useEffect及其依赖数组,你可以编写出更加清晰、易于维护且遵循最佳实践的React应用程序。

如何理解 Effect Event API?

Effect Event API 永远在 Effect 函数中调用执行。
如果一个交互,直接导致了A状态的变化,而A的变化又会导致B的变化,但是 C的变化也有可能会导致B的变化(可能是不同方面的变化),
此时,对A的变化应放置在事件处理程序中, 而由于A变化导致的B变化,应该放在Effect函数中,
但是 C的变化可能也会影响B的其他方面的变化,此时, 应该加一层封装方法,即 将 C对B的变化 封装为 Effect Event ,
然后再在由A导致的B变化的Effect中调用。
例如, 切换聊天室,会导致连接断开然后重新建一个新的连接,同时弹出消息提示,连接断开了或者连接成功了。而这个消息提示的样式是由 当前激活的主题色决定的。
但是主题色的切换,又不会,也不应该影响聊天室的切换连接,因此不能将主题色的变化,放到Effect的依赖收集中。
这个场景中,应该将消息提示的样式根据当前激活的主题色变化,这一个变化,放到 Effect Event中。
而连接成功或者断开的方法应该放到Effect中,在Effect中执行 Effect Event。

又比如,页面的url一发生改变,就需要记录一个访问日志,并将购物车的商品数量发送给后台,而购物车中商品的数量是跟随添加或者移除购物车的商品进行变化的。
商品数量变化不应该需要触发记录访问日志的动作,因此,商品数量不应该作为Effect的依赖收集,影响Effect的执行次数。
即 A 的变化会导致B,但是B的一些属性又是由 C的变化所决定的。A和C其实没有本质的关联,而是由于B,才产生的间接联系。
这里的A 应该放在事件处理程序中,然后,B由C变化应该封装成 Effect Event, 最后在 A对B的Effect中进行调用

还比如, 页面上有个计数器,每一秒增加1,但是有个控制器,通过控制步长,计数器会每秒增加不同的数值
这个场景中,应该将控制器控制步长,这一个变化作用,作为一个交互事件,即放到事件处理程序中
每秒增加的步长变化放到Effect Event中,计数的逻辑放到Effect中


__EOF__

本文作者龙陌
本文链接https://www.cnblogs.com/longmo666/p/18005690.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   龙陌  阅读(17)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
历史上的今天:
2021-02-04 HashMap 底层实现原理是什么?
点击右上角即可分享
微信分享提示