
大概的代码如下:
interface Props { onChange?: () => void; } const MyComponent: React.FC<Props> = (props: Props) => { const { onChange } = props; const [checkedItems, setCheckedItems] = React.useState([]); React.useEffect(() => { onChange?.(checkedItems); }, [checkedItems, onChange]); } 能够正常工作,但是有一个问题,该 effect 每次 re-render 的时候,都会被调用。
因为 parent component 也是一个 function component ,所以,onChange 的 reference/pointer 总是在变。
我想把 onChange 从 dependency list 里面去掉,但 ts/eslint 一直警告,不让我这么干...
我现在能够做的就是在 parent component 里面,使用 useCallback 或者 useMemo 记住这个方法,再传递给子组件 MyComponent's props
const OnChange= useCallback(()=> {}); 虽然能够解决问题,但要求 parent 在使用 MyComponent 的时候,知道这个细节。我觉得这样不是好的 design 。
有没有什么办法改进一?难道要去 disable ts/eslint 的警告?
谢谢!
1 noe132 2022 年 6 月 1 日 这是 parent 的问题 so |
2 noe132 2022 年 6 月 1 日 还有就是不把 onChange 加到 deps 。说明你的 lint 规则太严格了。 另外还可以不要用 useEffect 来检测变化,直接让 onChange 冒泡,在每次 setCheckedItems 时手动调用 onChange |
3 xiaojun1994 2022 年 6 月 1 日 建议看看 ahooks 的 useMemoizedFn ,或者 useLatest ,或者自己用 ref 把 onChange 存一下 |
4 otakustay 2022 年 6 月 1 日 最正规的是别这么玩,你在哪 setState 就在哪调用 onChange 如果硬要这么玩,就拿 useRef 包一下 onChange ,可以参考这个: https://github.com/ecomfe/react-hooks/blob/master/packages/intended-lazy/src/index.ts 但其实你是不知道外面 onChange 变了到底是真的逻辑变了,还是其实不想变的,所以这么做是不安全的 |
5 Leviathann 2022 年 6 月 1 日 就是 useCallback 啊 react 是通过浅比较判断是否要走优化的 render path 你从 useState 里拿到的 setXXX 不会变实际上也就是相当于它帮你 useCallback 了 |
6 |
7 fengfuliu 2022 年 6 月 1 日 不过首先应该考虑不要用 useEffect 来检测变化,直接让 onChange 冒泡 |
8 zhouyg 2022 年 6 月 1 日 你本意是想 checkedItems 变化的时候通知外面,这样的写法是有点接近 vue 的 watch 思想,但在 react 里,你并不依赖 onChange ,应该封装一下 setCheckedItems ,在 set 的时候同时 onChange 到外部 |
9 CodingNaux 2022 年 6 月 1 日 1. 为什么要用 useEffect 去触发 onChange,什么地方 setCheckedItems ,什么地方调 onChange 不行吗 2. useMemoizedFn 或者 usePersistFn ,直接学习他们源代码,体会下为什么这么写 https://github.com/alibaba/hooks/blob/master/packages/hooks/src/useMemoizedFn/index.ts https://github.com/alibaba/hooks/blob/v2.10.14/packages/hooks/src/usePersistFn/index.ts |
10 seki 2022 年 6 月 1 日 两个问题 本来就是 parent 要负责传入的函数 ref 一致的,这个 design 是对的 这个 checkItems 状态是不必要的,如果把这个组件当成 controlled 的,数据流上会更清晰 |
11 shenyu1996 2022 年 6 月 1 日 onChange 可以去掉 onchange 变化组件也会刷新,useEffect 内也可以保证是最新的 onchange 当然主动触发 onchange 更符合直觉 |
12 zhuweiyou 2022 年 6 月 1 日 封装一下 setCheckedItems , 改变 items 的同时 也调用 onChange, 不使用 useEffect. 要么就是 useCallback 了 |
13 lujjjh 2022 年 6 月 1 日 有人提到最佳实践,最佳实践不是一成不变的,有时候 React 里的最佳实践只是因为目前只能这么做。 可以关注下 useEvent 这个 RFC ,链接指向的部分就是在描述类似的问题: https://github.com/reactjs/rfcs/blob/useevent/text/0000-useevent.md#useeffect-shouldnt-re-fire-when-event-handlers-change |
14 yazoox OP |
16 shenjo 2022 年 6 月 2 日 这样写逻辑都有点不对吧,onChange 变化就执行 onChange 函数? 你本意应该是 checkedItems 发生变化才会回调 onChange 。如果 onChange 里会做一些副作用操作不久 gg 了.比如 <MyComponent OnChange={()=> window.count++}/>。当然我举的例子不太合适。所以我觉得应该像其他楼里兄弟说的,考虑在 setCheckedItem 的同时去触发 onChange ,这才符合你想要表达的意思。 |
17 AyaseEri 2022 年 6 月 2 日 实践的角度上,你应该无视 lint 的报错,将 onChange 从 dep list 里去掉。 |