1 引言
本期精读的文章是: 8 React conditional rendering methods https://blog.logrocket.com/conditional-rendering-in-react-c6b0e5af381e
介绍了八种 React 条件渲染方式.
模版条件渲染非常常见, 遇到的时候往往会随机选择一种方式使用, 那么怎么写会有较好的维护性呢? 先一起了解下有哪八种条件渲染方式吧!
2 概述
IF/ELSE
既然 JSX 支持 js 与 html 混写, 那么交替使用就能解决条件渲染的问题:
- function render() {if (renderComponent1) {
- return <Component1 />;
- } else {
- return <div />;
- }
- }
- return null
如果不想渲染空元素, 最好使用 null 代替空的 div:
- function render() {
- if (renderComponent1) {
- return <Component1 />;
- } else {
- return null;
- }
- }
这样对 React 渲染效率有提升.
组件变量
将组件赋值到变量, 就可以在 return 前任意修改它了.
- function render() {
- let component = null;
- if (renderComponent1) {
- component = <Component1 />;
- }
- return component;
- }
三元运算符
三元运算符 https://en.wikipedia.org/wiki/?: 的语法如下:
condition ? expr_if_true : expr_if_false
用在 JSX 上也很方便:
- function render() {
- return renderComponent1 ? <Component1 /> : null;
- }
但三元运算符产生嵌套时, 理解成本会变得很高.
&&
这个是最常用了, 因为代码量最少.
- function render() {
- return renderComponent1 && <Component1 />;
- }
- IIFE
IIFE 含义是立即执行函数, 也就是如下代码:
- (function myFunction(/* arguments */) {
- // ...
- })(/* arguments */);
当深陷 JSX 代码中, 又想写一大块逻辑时, 除了回到上方, 还可以使用 IIFE:
- function render() {
- return (
- <div>
- {(() => {
- if (renderComponent1) {
- return <Component1 />;
- } else {
- return <div />;
- }
- })()}
- </div>
- );
- }
子组件
这是 IIFE 的变种, 也就是把这段立即执行函数替换成一个普通函数:
- function render() {
- return (
- <div>
- <SubRender />
- </div>
- );
- }
- function SubRender() {
- if (renderComponent1) {
- return <Component1 />;
- } else {
- return <div />;
- }
- }
IF 组件
做一个条件渲染组件 IF 代替 js 函数的 if:
<If condition={true}>
<span>Hi!</span>
</If>
这个组件实现也很简单
- const If = props => {
- const condition = props.condition || false;
- const positive = props.then || null;
- const negative = props.else || null;
- return condition ? positive : negative;
- };
高阶组件
高阶组件, 就是返回一个新组件的函数, 并且接收一个组件作为参数.
那么我们就能在高阶组件里写条件语句, 返回不同的组件即可:
- function higherOrderComponent(Component) {
- return function EnhancedComponent(props) {
- if (condition) {
- return <AnotherComponent {...props} />;
- }
- return <Component {...props} />;
- };
- }
3 精读
这么多方法都能实现条件渲染, 那么重点在于可读性与可维护性.
比如通过调用函数实现组件渲染:
<div>{renderButton()}</div>
看上去还是比较冗余, 如果使用 renderButton getter 定义, 我们就可以这么写它:
<div>{button}</div>
其实我们想要的就是 button, 而不是 renderButton. 那么还可以进一步, 干脆封装成 JSX 组件:
<div>
<Button />
</div>
是否要付出这些努力, 取决于应用的复杂度. 如果应用复杂度非常高, 那你应当尽量使用最后一种封装, 让每个文件的逻辑尽量独立, 简单.
如果应用复杂度比较低, 那么注意不要过度封装, 以免把自己绕进去.
所以看来这又是一个没有固定答案的问题, 选择何种方式封装, 取决于应用复杂度.
应用复杂度
对任何代码封装, 都会增加这段 连接逻辑 的复杂度.
假定无论如何代码的复杂度都是恒定不变的, 下面这段代码, 连接复杂度为 0, 而对于 render 函数而言, 逻辑复杂度是 100:
- function render() {
- if (renderComponent) {
- return isOk ? <Component1 /> : <Component2 />;
- } else {
- return <div />;
- }
- }
下面这段代码拆成了两个函数, 逻辑复杂度对 render SubComponent 来说都是 50, 但连接复杂度是 50:
- function render() {
- if (renderComponent) {
- return <SubComponent>;
- } else {
- return <div />;
- }
- }
- function SubComponent() {
- return isOk ? <Component1 /> : <Component2 />
- }
可以看到, 我们通过函数拆分, 降低了每个函数的逻辑复杂度, 但却提高了连接复杂度.
下面来做一个比较, 我们假设一个正常的程序员, 可以一次性轻松记忆 10 个函数. 如果再多, 函数之间的调用关系就会让人摸不着头脑.
应用较小时
在应用代码量比较小时, 假设一共有 10 个函数, 如果做了逻辑抽象, 拆分出了 10 个子函数, 那么总逻辑复杂度不变, 函数变成了 20 个.
此时小王要修改此项目, 他需要找到关键代码的位置.
如果没有做逻辑抽象, 小王一下子就记住了 10 个函数, 并且很快完成了需求.
如果应用做了逻辑抽象, 他需要理解的逻辑复杂度是不变的, 但是要读的函数变成了 20 个. 小王需要像侦探一样在线索中不断跳转, 他还是只找了 10 个关键函数, 但一共也就 20 个函数, 逻辑并不复杂, 这值得吗?
小王心里可能会嘀咕: 简单的逻辑瞎抽象, 害我文件找了半天!
应用较大时
此时应用代码量比较大, 假设一共有 500 个函数, 我们不考虑抽象后带来的复用好处, 假设都无法复用, 那么做了逻辑抽象后, 那么总逻辑复杂度不变, 函数变成了 1000 个.
此时小王接到了需求, 终于维护了一个大项目.
小王知道这个项目很复杂, 从一开始就没觉得能理解项目全貌, 所以把自己当作一名侦探, 准备一步步探索.
现在有两种选择, 一种是在未做逻辑抽象时探索, 一种是在做过逻辑抽象后探索.
如果没做逻辑抽象, 小王需要面对
500
个这种函数:
- function render() {
- if (renderComponent) {
- return isOk ? <Component1 /> : <Component2 />;
- } else {
- return isReady ? <Component3 /> : <Component4 />;
- }
- }
如果做了逻辑抽象, 小王需要面对
1000
个这种函数:
- function render() {
- if (renderComponent) {
- return <Component1And2 />;
- } else {
- return <Component3And4 />;
- }
- }
在项目庞大后, 总函数数量并不会影响对线索的查找, 而总线索深度也几乎总是固定的, 一般在 5 层左右.
小王理解 5 个或 10 个函数成本都差不多, 但没有做逻辑抽象时, 这 5 个函数各自参杂了其他逻辑, 反而影响对函数的理解.
这时做逻辑抽象是合适的.
4 总结
所以总的来说, 笔者更倾向使用子函数, 子组件, IF 组件, 高阶组件做条件渲染, 因为这四种方式都能提高程序的抽象能力.
往往抽象后的代码会更具有复用性, 单个函数逻辑更清晰, 在切面编程时更利于理解.
当项目很简单时, 整个项目的理解成本都很低, 抽象带来的复杂度反而让项目变成了需要切面编程的时候, 就得不偿失了.
总结一下:
当项目很简单, 或者条件渲染的逻辑确认无法复用时, 推荐在代码中用 && 或者三元运算符, IIFE 等直接实现条件渲染.
当项目很复杂时, 尽量都使用 子函数, 子组件, IF 组件, 高阶组件 等方式做更有抽象度的条件渲染.
在做逻辑抽象时, 考虑下项目的复杂度, 避免因为抽象带来的成本增加, 让本可以整体理解的项目变得支离破碎.
5 更多讨论
讨论地址是: 精读React 八种条件渲染 . Issue #90 . dt-fe/weekly https://github.com/dt-fe/weekly/issues/90
如果你想参与讨论, 请点击这里 https://github.com/dt-fe/weekly , 每周都有新的主题, 周末或周一发布.
来源: https://juejin.im/post/5b285c0d5188257494641d0b