react不常用功能补漏
前一阵过了一遍react文档, 记录看到的几个不太常用api.
pure component与should component update
PureComponent
PureComponent其实就对应着pure function. (是我这次才恍然大悟的)
也就是初中第一次学到函数的概念: y = f(x), 一个x永远对应一个y.
这里PureComponent的输入是state和props, 输出是dom. 只要相同的state和props永远对应相同的dom, 就可以设置为PureComponent.
shouldComponentUpdate
PureComponent在进一层的实现上是shouldComponentUpdate(scu)的封装, 在scu的时候对上次的state和props作了浅比较. 如果浅比较结果相同会阻止即将进行的渲染.
文档里的图表很清晰地说明, props和state的变化会触发scu, 如果scu返回false就不走render了.
文档看到这里, 我问了同事, 是不是大多数组件都可以改成PureComponent. 同事扔给了我一个链接, 那个链接我没有记, 自己写了个类似的demo. 这个demo把Component改成PureComponent就会出现bug.
mutate state与pure component
因为demo中的setState是这样写的:
1 | this.setState(state => { |
react的state是要求immutable的. 比较初级的程序员经常会犯的这个错误.
但被问到mutate state有何不可, 我一般说有2个问题, 第一不可测试. 第二导致的奇怪bug很难debug.
这里就发现了个mutate state导致的bug.
因为直接改动了原state的属性, 在scu的时候, 新state和原state中变化的那个值是指向同一个地址的, 就导致了scu误判返回了false阻止render.
还是得写成immutable的形式来避免这个bug:
1 | this.setState(state => { |
react-redux的connect与mutate state
而我们大多项目是用redux的, 碰巧redux也是immutable的设计. 于是写了个demo来试一下react-redux
的connect()
方法有没有做类似的优化.
于是又巧了. connect()
内部也用了浅比较来优化. 于是产生了和上一节一样的问题, 解决方案也相同, 就不赘述了.
再挖了一下connect()
, 发现第四个参数可以传参数的, 在demo的第57行这个属性设置为===
就可以在mutate state的情况下得到期望的结果了. (默认值是浅比较)
get derrived state from props与受控组件
当组件实现了getDerrivedStateFromProps, state和props就有了一层绑定关系. 在不判断条件的情况下, 一个普通的组件就会从非受控组件变成受控组件.
在对受控/非受控组件概念不请的时候, 就会在这里写不合适的代码, 而造成非期望的bug和不可测试.
在我不理解受控/非受控组件前, 在看到一些ui库的表单组件和表格组件的selected相关api会觉得反人性设计.
(具体案例不提了太基础了)
render props
render props的定义是: 调用props的函数作为render返回的一部分.
所以render props的使用方会感觉: 使一个prop返回jsx元素, render props的构造组件会感受: render的返回值中包含了props的调用.
render props其实不一定要叫render, 也可以是其他的. 于是想到了一个奇妙的元素: children. children可以直接写在标签中, 就想到了之前使用的AutoSizer
原来是用了render props.
react的特点(与vue比较)
react三大抽组件方式, rp, hoc, hooks, 让人感觉最明显的是: react的组件就是一个函数(vue使用组件还需要注册, 很明显能感受到). react能更灵活的运用js的特性, rp, hoc, hooks其实都不是react特性, 而是js特性.
另外, react的使用者要自己做优化, 或者说是遵循一定规范. 不然会导致预期外的bug和性能损耗, 而vue帮用户做了优化. (但自动优化本身是耗性能的)
这里说一点经验中总结的规范, 要把不同更新频率的页面部分分成不同的组件, 并用scu阻断渲染树. 这个在用redux connect的时候也要注意, 不要用点点点引入页面不相关的数据.
原因是: react的render方法只能整体执行. (vue的render方法只是生成ast, 下一步还会patch. 而react???). 下面举一个具体例子:
组件A 包含 组件B, 组件C. B, C分别依赖A的state.
当setState改变C依赖的state时, B也会被重新渲染.
所以方法是: 用pureComponent把B包起来, 这也是为什么ui组件库会尽量用pureComponent.
插件互用
react跨组件的状态管理有redux, mobx, hox, recoil等. 而如果只是传递一个事件, 其实并不需要状态管理.
拿redux说, 其实dispatch就是直接操作reducer的. 而redux和react有关系的地方就是connect, 要通过redux传递事件只能去改变状态, 再监听props. 这显得非常不合理.
所以用Vue的event bus就可以, event bus就是一个闭包, 把组件存在闭包里, 在触发事件的时候调用就行了. 当然也自己写个30行的event bus.
反过来也是的, vue也可以用redux. 之前在小程序里用的时候对vue和react的理解还比较局限. 希望以后对js和框架的理解能更深刻.
(本文完)
如果你可以 点击这个链接打赏我5毛来鼓励我, 非常感谢.
本文遵循 cc协议
你可以在注明出处和非商用的前提下任意复制及演绎