大概的代码如下:
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-06-01 09:53:36 +08:00
这是 parent 的问题 so
|
2
noe132 2022-06-01 09:57:48 +08:00
还有就是不把 onChange 加到 deps 。说明你的 lint 规则太严格了。
另外还可以不要用 useEffect 来检测变化,直接让 onChange 冒泡,在每次 setCheckedItems 时手动调用 onChange |
3
xiaojun1994 2022-06-01 09:59:10 +08:00
建议看看 ahooks 的 useMemoizedFn ,或者 useLatest ,或者自己用 ref 把 onChange 存一下
|
4
otakustay 2022-06-01 10:04:40 +08:00 1
最正规的是别这么玩,你在哪 setState 就在哪调用 onChange
如果硬要这么玩,就拿 useRef 包一下 onChange ,可以参考这个: https://github.com/ecomfe/react-hooks/blob/master/packages/intended-lazy/src/index.ts 但其实你是不知道外面 onChange 变了到底是真的逻辑变了,还是其实不想变的,所以这么做是不安全的 |
5
Leviathann 2022-06-01 10:18:42 +08:00
就是 useCallback 啊
react 是通过浅比较判断是否要走优化的 render path 你从 useState 里拿到的 setXXX 不会变实际上也就是相当于它帮你 useCallback 了 |
6
fengfuliu 2022-06-01 10:40:28 +08:00
用 useCallback 包没问题的 一般来说子组件的 props 如果是引用类型,都需要子组件 memo+父组件 useCallback
|
7
fengfuliu 2022-06-01 10:42:32 +08:00
不过首先应该考虑不要用 useEffect 来检测变化,直接让 onChange 冒泡
|
8
zhouyg 2022-06-01 10:57:40 +08:00
你本意是想 checkedItems 变化的时候通知外面,这样的写法是有点接近 vue 的 watch 思想,但在 react 里,你并不依赖 onChange ,应该封装一下 setCheckedItems ,在 set 的时候同时 onChange 到外部
|
9
CodingNaux 2022-06-01 11:12:17 +08:00
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-06-01 11:16:09 +08:00
两个问题
本来就是 parent 要负责传入的函数 ref 一致的,这个 design 是对的 这个 checkItems 状态是不必要的,如果把这个组件当成 controlled 的,数据流上会更清晰 |
11
shenyu1996 2022-06-01 12:02:10 +08:00
onChange 可以去掉
onchange 变化组件也会刷新,useEffect 内也可以保证是最新的 onchange 当然主动触发 onchange 更符合直觉 |
12
zhuweiyou 2022-06-01 12:11:26 +08:00
封装一下 setCheckedItems , 改变 items 的同时 也调用 onChange, 不使用 useEffect.
要么就是 useCallback 了 |
13
lujjjh 2022-06-01 12:36:50 +08:00 2
有人提到最佳实践,最佳实践不是一成不变的,有时候 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-06-02 15:55:45 +08:00
这样写逻辑都有点不对吧,onChange 变化就执行 onChange 函数? 你本意应该是 checkedItems 发生变化才会回调 onChange 。如果 onChange 里会做一些副作用操作不久 gg 了.比如 <MyComponent onChange={()=> window.count++}/>。当然我举的例子不太合适。所以我觉得应该像其他楼里兄弟说的,考虑在 setCheckedItem 的同时去触发 onChange ,这才符合你想要表达的意思。
|
17
AyaseEri 2022-06-02 17:53:17 +08:00
实践的角度上,你应该无视 lint 的报错,将 onChange 从 dep list 里去掉。
|