请教一个 react hook 的问题

280 天前
 Asuler

关于 react 的 useEffect ,如果我要监听一个值,并且在值变化的时候做一些函数调用

  useEffect(()=>{
    if(type === aaa){
      aHandle();
    }else if(type === bbb){
      bHandle();
    }
  }, [type])

那我就需要把 aHandle 和 bHandle 放入依赖项中,否则 eslint 或者 sonarlint 会报警告

  useEffect(()=>{
    if(type === aaa){
      aHandle();
    }else if(type === bbb){
      bHandle();
    }
  }, [type, aHandle, bHandle])

那这样岂不是每次 render ,都会重新生成新的 ahandle 和 bHandle ,每次都会触发 useEffect 吗?

我知道有 useCallback 和 useRef 可以解决函数在每次 render 都重新生成的问题,但问题是假如我在 aHandle 里去调接口,也要获取很多放在 state 中的值作为参数,那么 useCallback 还得把那些值全部放在 useCallback 的依赖项里,搞得越来越复杂了。

难道只能用 useRef 或者基于 useRef+useCallback 封装的一些 hook ,把每个 aHandle 或者 bHandle 给套上吗。

有没有更优雅一点的写法,我想要有一个 useXXXEffect,可以只监听一个 type ,在这里面获取到的其他值都是最新的,不再额外需要传入 aHandle 或者 bHandle 。

有没有这样的 hook 或者封装成这种效果的 hook

3167 次点击
所在节点    React
53 条回复
realJamespond
280 天前
用 ref 是正解,不过如果 aHandle ,bHandle 没有其他依赖项也可以正常引用,就是一直保持组件渲染第一次的地址
Radix10
280 天前
这个应该不需要 hook 吧
super996
280 天前
在切换 type 的那个 onChange/onSelect/onClick 做,
onChange={(type: string) => {
if (type === 'aaa') {
aHandle()
} else if (type === 'bbb') {
bHandle()
}
// ...
}}
Marthemis
280 天前
meta575 super996 是正解。useEffect 是处理函数的副作用,而不是去监听值(这两者在某些场景下容易混淆)
wang4012055
280 天前
函数式编程理念是函数无副作用,所以你的调用函数最好不要引用外部状态,使用传参方式.这样使用 callback 就没什么问题了.
jinliming2
279 天前
不知道你的 aHandle 和 bHandle 的具体逻辑,不过仅目前的这段代码的逻辑来说,按照我的思路,我会把 aHandle 和 bHandle 直接写成 useEffect 。
useEffect(() => {
if (type !== aaa) return;
// aHandle 的函数体,直接处理,而不是调函数
}, [type]);
useEffect(() => {
if (type !== bbb) return;
// bHandle 的函数体,如果要异步处理,就立即执行包一下
let cancel = false;
(async () => {
await xxx;
if (cancel) {
return;
}
//...
})();
return () => {
cancel = true;
};
}, [type]);
IvanLi127
279 天前
按你这个需求,好像直接把那一行的 lint 禁用掉好了……
Terry166
279 天前
Effect Events are not reactive and must always be omitted from dependencies of your Effect. This is what lets you put non-reactive code (where you can read the latest value of some props and state) inside of them.
参考文档:
https://react.dev/learn/separating-events-from-effects#reading-latest-props-and-state-with-effect-events
w4ngzhen
279 天前
```jsx
const App = () => {
const [count, setCount] = useState(1);
const handle = () => {
console.log('做些事');
}
useEffect(() => {
handle();
}, [count])
return <button onClick={() => setCount(count + 1)}>Add One</div>
}
```

感觉大家没有回答到点上啊。首先,React 中的函数式组件,每“运行”一次,是一个时刻的结果。比如上面的 App 函数,完成一次加载以后。实际上就是运行了一次 App 函数,对于第一次视为 t1 ,t1 流程是:
1. 初始化了一个 count 的 state
2. 通过变量`handle`定义了一个函数
3. 执行了一次 useEffect
4. 返回了一个`<button />`

这里面最关键的点是步骤 3 执行 useEffect 。在第一次运行的时候,这个匿名方法:

```js
// t1 时刻的匿名函数
() => {
handle(); // t1 时刻的 handle 变量
}
```

被 React 存放到了内部,并且它捕获了 t1 时刻的变量`handle`,并且,通过`[count]`定义了依赖项。并且,t1 的匿名函数会执行一次。

当你点击按钮的时候,由于调用了 setCount ,在上述场景下,会导致 App 重新执行一次,我们把第二次执行的流程视为 t2 。它的过程是:

1. 由于第 2 次了,useState 拿到的值不再是初始值,而是上一次 set 的值,在上面的例子是 2 ;
2. 通过变量`handle`定义了一个函数。这里的 handle ,跟 t1 阶段的 handle 完全是两个变量,它们仅仅是名字一样,代码块一样而已。
3. 执行一次 useEffect 。此时,生成了一个 t2 时刻的匿名函数:

```js
// t2 匿名
() => {
handle(); // 这里的 handle 也是 t2 时刻的 handle ,跟 t1 的 handle 没有任何关系
}
```

此时,t1 的 count = 1 与 t2 的 count = 2 不一样了,所以,useEffect 中的匿名函数( t2 版本)会执行一次,handle 自然就是 t2 版本的 handle 。

另外,上述场景中的 handle 能用 count 这个 state 吗?当然可以,因为 t2 时刻的 handle 捕获的是 t2 时刻的 count 。
w4ngzhen
279 天前
另外,useEffect 一定要分两步看:它“吃”了一个匿名函数,但是不意味着它立刻会调用;调用的时机取决 React 所的定义的依赖判定。
w4ngzhen
279 天前
“那我就需要把 aHandle 和 bHandle 放入依赖项中,否则 eslint 或者 sonarlint 会报警告” —— eslint 具体的警告是什么?感觉你这种场景是很常见的。aHandle 和 bHandle 有什么特殊之处吗?
w4ngzhen
279 天前
@meta575 你说的这种也是正确的处理方式,通过封装无状态的方法,在多个 type 变化的地方调用也是解决方案。
wpzz
279 天前
useEffect 不要这么写,如果里面代码量上来了,refs [] 会监听超级多变量和函数。

这里面函数又套了 useCallback ,维护会爆炸。
Makabaka01
279 天前
把 lint 禁掉就行了,hooks lint 不太智能,挺烦的
neotheone2333
279 天前
非常的典型的 useEvent 场景,直接用 useMemoizedFn 就行了。否则就放在 onClick 里面做
leoskey
279 天前
正如前排所说你可能不需要使用 useEffect 。useEffect 不应该用于监听某个东西的变化,应该用于组件时卸载清理的工作,例如取消订阅。

官方文档专门对这种情况进行了说明 https://react.dev/learn/you-might-not-need-an-effect
leoskey
279 天前
非要这么写,那就按你第一份代码,把那一行的 lint 禁用
otakustay
279 天前
最新版本是 useEffectEvent ,以前版本是用 useRef 存它,然后再补个 useEffect 更新它
connection
279 天前
@otakustay 以前都得这么干 TAT
iOCZS
279 天前
主要是 aHandle 是不是纯的,它是纯的话,你甚至可以拿到组件外边去。如果不纯的话,它依赖什么数据?依赖变化的时候,它是需要更新的。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1028021

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX