这一年,给团队Code Review所发现的问题

66 阅读5分钟

背景

过去一年偶尔在Gitlab上给团队成员做Code Review并发现了一些问题,并陆续记录下来。我把这些问题进行总结归类,整理成文档。

React基础问题

useEffect和useMemo

假设场景:组件中的color值需要经过initColor计算而来,有三种实现

第一种:直接写

// ...

// 不推荐:任何组件状态更新都会重新计算颜色
const color = calcColor(initColor);
return <div> 当前颜色:{ color } </div>;
// ...

第二种:useEffect

// ...
// 只有initColor变化才计算
const [color, setColor] = useState();
useEffect(() => {
  setColor(calcColor(initColor));
}, [initColor]);

return <div> 当前颜色:{ color } </div>;
// ...

第三种:useMemo

// ...
// 只有initColor变化才计算
const color = useMemo(() => calcColor(initColor), [initColor]);
return <div> 当前颜色:{ color } </div>;
// ...

第二种和第三种的区别:useMemo在渲染时就执行。useEffect在渲染之后再执行然后更新color。执行时机上第一种和第三种相同,第三种是第一种的基础上做了优化减少重复执行。

编码规范

if else 问题

多层嵌套if else 互斥的操作用if else 而不是多个if

指令式和声明式

// 指令式
let arr = [1, 2, 3, 4, 5];
let squares = [];
for(let i = 0; i < arr.length; i++) {
    squares.push(arr[i] * arr[i]);
}
// 声明式,更简洁
let arr = [1, 2, 3, 4, 5];
let squares = arr.map(num => num * num);

消除重复!

写代码的时候总是会有意无意的出现重复,下面这两个例子是逻辑判断的重复逻辑,整合之后可以让代码更简洁:

// 不推荐:
isActive ? 'opacity(1)' : isSelected ? 'opacity(1)' : 'opacity(0.5)'
// 推荐:
isActive || isSelected ? 'opacity(1)' : 'opacity(0.5)' 
// 不推荐
if (value) {
  this.setState({ title: value, createDisabled: false });
} else {
  this.setState({ title: value, createDisabled: true });
}
// 推荐
this.setState({ title: value, createDisabled: !value });

真是无处不在的重复,再比如

函数传参:

// 传了item就不要传key了
onClick={() => onClick({ item, key: item.key })}

样式设置:

 <div
    className={{
      [styles.UnSelected]: !isSelected,
    }}
    {/* 重复出现isSelected。这里className和style应该只留一种写法 */}
    style={{
      color: isSelected ? '#FFFFFF' : '#666666'
    }}
  />

还是要对重复保持敏感

命名问题

通用组件/方法的命名需要抽象,使其与业务无关。应该脱离特殊场景从普遍性的角度去考虑

例子:

// 改变color的透明度
// 虽然业务中是把透明度变浅。但该函数可以加深透明度呀
const getLightColor = (color: string, opacity: number) => {
// ...

// 可以改成
const changeColorOpacity = (color: string, opacity: number) => {

typescript中 " | " 的使用例子

不推荐:

// 危险!
interface IActionsProps {
  selectedBranchId: string | bigint;
  onSwitchBranch: (branchId: string | bigint) => void;
}

业务说明:IActionsProps是一个组件的props类型定义。selectedBranchId和onSwitchBranch函数的branchId参数都有可能是string 或 bigint类型,但它们需要是相同类型

推荐:

type IActionsProps =
  | {
      selectedBranchId: string;
      onSwitchBranch: (branchId: string) => void;
    }
  | {
      selectedBranchId: bigint;
      onSwitchBranch: (branchId: bigint) => void;
    };

类型约束后,这两者一定是相同类型

下面开始是按 review 的时间分类

2022/05/20

  1. 状态抽象,不同的视图可以由同一个状态决定,那应该只维护一个状态,用同个动作去控制。例如以下场景:根据页面路由来改变Tab组件状态和页面内容

image.png

那么数据流应该是:

image.png 而不是:

image.png

  1. 用更符合强类型语言/函数式的规范解决ts类型问题

不推荐:

commitList.forEach((commit: InterfaceCommit) => {

  // 在dfs中修改commit从而也修改commitList,此时commit类型错误

  dfs(commit);

});

推荐:

const commitListForSort = commitList.map((commit: InterfaceCommit) => {

  // dfs不再直接修改commit而是返回新的commit

  return dfs(commit);

});

2022/04/29

  1. 如果某个元素,在点击时添加了某个click事件,但同时又触发了该事件,可以考虑添加一个stopPropergation方法阻止冒泡。
  2. 组件接受一个字符串参数时,可以采用以下示例的方式。
//不推荐

interface ITipPopProps {
 direction?: Direction | string;
}

enum Direction {
 TOP = 'Top',
 RIGHT = 'Right',
 BOTTOM = 'Bottom',
 LEFT = 'Left',
}

//推荐,对使用者更友好 ✅

interface ITipPopProps {
 direction?: Direction;
}

type Direction = 'Top'|'Right'|'Bottom'|'Left'

2022/04/02

  1. Css 类覆盖问题,尽量不要使用!important
  2. 复杂组件要尽量细分成小组件,函数也一样

2022/02/18

  1. useEffect return问题
// 错误

useEffect(() => {
  if (condition) {
    // ...
  }

  return () => {
    // ...
  }
}, [condition]);


// 正确
useEffect(() => {
  if (condition) {
    // ...
    return () => {
    // ...
    }
  }
}, [condition]);
  1. 用lodash flow 方法处理连续调用的问题,更优雅

2022/02/11

  1. 微前端对于当前项目是否必要

微前端解决的问题: 子项目技术栈无关(研发效率)、子项目预加载(用户体验)

目前不同子项目独立开发部署,通过nginx代理集合不同子项目,实际已经解决了第一个问题。第二个问题当前痛点不明显。

微前端解决方案:single-spagarfish

微前端资料:swearer23.github.io/micro-front…

  1. drive.js 实现镂空效果原理:

加一层遮罩,给需要高亮的元素设置 position: relative 以及一个较高z-index。用js递归重置高亮元素的所有父级的z-index为auto,

对应源码 github.com/kamranahmed…

获取元素计算后css属性值: Window.getComputedStyle()

2021/12/31

代数效应

  1. react suspense 与 传统方式对比

codesandbox.io/s/fragrant-…

codesandbox.io/s/frosty-he…

  1. react hook 中的应用

react资料: react.iamkasong.com/#导学视频

2021/12/24

  1. 不使用匿名函数,增加代码可读性, 案例对比:
// 不推荐写法
useEffect(() => {
  if (!value) {
    setResultList([]);
    return;
  }
  const resultList = searchResult.filter((search) => {
    return path.basename(search.name).indexOf(value) != -1;
  });
  setResultList(resultList);
}, [value]);

// 推荐写法
useEffect(() => {
  const search = (value) => {
    if (!value) {
      return [];
    }
    const resultList = searchResult.filter((search) => {
      return path.basename(search.name).indexOf(value) != -1;
    });
    return resultList;
  };

  setResultList(search(value));
}, [value]);
  1. 使用纯函数,减少副作用,函数返回值只和输入值有关
// 不推荐
const toHelpSearchPage = (baseName) => {
  // 其中value为react组件中定义的state
  router.push(/help/search?search=${baseName ? baseName : value})
}

// 推荐
const toHelpSearchPage = (baseName, value) => {
  // 其中value为react组件中定义的state
  router.push(/help/search?search=${baseName ? baseName : value})
}