Closed chi-gao closed 11 months ago
field 的 onChange 在你的 demo 里我看已经生效了,是你的子组件没有重新渲染去拿到最新的 field 值
是的, 正常情况(没有使用observer)时,应该是 onChange的时候同时触发子组件的重新渲染。 但使用了observer之后, 重新渲染好像不生效了。
所以请教一下,在使用mobx的场景下, 应如何确保子组件的正常渲染?
跟 mobx 没关系,你这里没有 mobx 也不会自动渲染Child,因为 field 拿到的是 Select,他只会让 Select 重新渲染,但是 Select 拿到的 props 是 Child 传给他的,但是 Child 并没有重新渲染。
本质是你 field 传入方式不对,你应该是让 Child 去接收 field.init 出来的属性,而不是里面的 Select
因为业务本身比较复杂,field表单分布在不同的子组件中,因此只能将field本身作为props传给各个子组件,这个是业务需要。
然后看了下field和 mobx-react-lite
中observer
的代码,搞明白了原因。
首先,为什么没有使用observer
包裹的组件可以响应 field的变化?
原因是当使用 field.init
初始化后的组件触发onChange
时, 会触发初始化field的组件(这里是父组件)的 reRender;然后父组件的reRender会触发子组件的reRender,因此子组件可以正常响应field值的变化,进而正确渲染。代码参考这里和这里
其次,为什么使用observer
包裹之后不响应field的变化了?
原因是 mobx-react-lite
中的observer
方法为了性能考虑,会使用 React.memo
包裹组件,包裹后的组件只有props变化才会触发重新渲染,因此父组件的reRender不会再引起子组件的reRender,因此无法响应正确的field值变化。代码参考这里
问题搞清楚了,那么怎么解呢?
其实不管是field
,还是mobx-react-lite
都没问题,可能确实不支持这种直接传递field的场景。最终原因是 子组件中表单项值变化只触发了父组件的重新渲染,子组件本身的props木有变化,因此重新渲染被阻断了。那么当表单项值变化时,同时引起子组件的props变化是不是就可以了?
基于这个思路,我们在父组件引入一个新的state,然后将这个state传给子组件用来响应field值的变化。最新代码参考这里:https://stackblitz.com/edit/stackblitz-starters-mvmzph?file=src%2FChild.tsx,src%2FApp.tsx 最终效果如下:
----------------------我是分割线-------------------------- 到这里为止, 问题是解了,但是方法还是相对比较tricky, 还是希望field本身能够支持这种用法,感谢感谢
我们无法 100% 控制子组件是否渲染,只能让 field 本身 attach 的组件进行重新渲染。比如你说的 props 变化触发子组件渲染,这只是因为你的子组件会对这个 props 变化产生重新渲染的行为,但不代表所有的子组件都是这样处理的。因为部门不知道子组件的shouldComponentUpdate 是怎样写的。
我们无法 100% 控制子组件是否渲染,只能让 field 本身 attach 的组件进行重新渲染。比如你说的 props 变化触发子组件渲染,这只是因为你的子组件会对这个 props 变化产生重新渲染的行为,但不代表所有的子组件都是这样处理的。因为部门不知道子组件的shouldComponentUpdate 是怎样写的。
是的,所以传递field实例本身可能不是一个好的实践。我这个例子我感觉正确的写法应该是:每个子组件包含自己的field,子组件利用value+onChange可以作为一个自定义的手控组件受父组件的控制
背景
父组件创建field实例, 然后将field作为props传递给子组件, 同时引入
Mobx
作为状态管理, 因此子组件需要使用observer
包裹,以响应 mobx store的变化。现象
子组件中的表单项,
onChange
时Field中的对应值变化了,但是表单本身值没有变化,应该是setState
未生效。如下图:复现地址
https://stackblitz.com/edit/stackblitz-starters-mvmzph?file=src%2FApp.tsx
步骤
直接切换下拉框的选项即可。
预期结果
正确响应onChange事件。