Code Review指南

31 阅读5分钟
  1. 必须】文件夹名用 - 形式命名文件夹名,如:reservation-card; React 组件用大驼峰;变量名用小驼峰

2.【必须变量的命名避免缩写

// bad 自我感觉良好的缩写:
let fName = 'jackie'; // 看起来命名挺规范,缩写,驼峰法都用上,ESlint各种检测规范的工具都通过,But,fName是啥?这时候,你是不是想说What are you 弄啥呢?
let rContent = 'willen'

// good 无需对每个变量都写注释,从名字上就看懂
let firstName = 'jackie'
let rightContent = 'willen';
  1. 必须】 兼容可能不存在的属性
// bad
const data = await getPeopleList(keyword, page, pageSize);
data.dataList.forEach() // 直接挂了

// good
const data = await getPeopleList(keyword, page, pageSize);
data?.dataList?.forEach() 
  1. 【推荐】单个文件代码行数不要超过200行
  2. **【必须】**避免使用魔法数字
// bad
if (type !== 0) {
  // TODO
}

// good
enum STATUS {
  // 就绪
  READY = 0,
  // 请求中
  FETCHING = 1,
  // 请求失败
  FAILED = 2,
}
  1. 必须路由懒加载
// bad
import OtherComponent from './OtherComponent';

// good
const OtherComponent = React.lazy(() => import('./OtherComponent'));
  1. 必须防止 xss 攻击

input,textarea 等标签,不要直接把 html 文本直接渲染在页面上,使用 DOMPurify 等过滤之后再输出到标签上

import DOMPurify from 'dompurify';
render(){
  <div
  dangerouslySetInnerHTML={{
    __html: DOMPurify.sanitize(htmlContent)
  }}
/>
}
  1. 【必须】整个应用程序中保持你的key 的唯一性

使用key 是很重要的,因为它可以帮助React识别已经改变的、被添加或被移除的确切元素。React需要弄清楚以前的UI和新的UI之间的差异,以便更新它。

  1. **【必须】**使用Fragement 来避免无用的嵌套标签
// bad
const InfoText = () => {
	
  return (
  	<div>
      <h1>Welcome!</h1>
      <p>This our new page, we're glad you're are here!</p>
    </div>
  )
}

// good
const InfoText = () => {
	
  return (
  	<>
      <h1>Welcome!</h1>
      <p>This our new page, we're glad you're are here!</p>
    </>
  )
}
  1. **【必须】**每个文件只包含一个 React 组件。
// src/pages/user.jsx
// bad
const Age = () => {
  const [age, setAge] = useState(18);
  return <div>{age}</div>;
};
const Name = () => {
  const [name, setName] = useState("echo");
  return <div>{name}</div>;
};
  1. 必须】避免嵌套渲染函数

组件应该只有属于它本身的功能,嵌套组件可以访问其父级的所有状态和数据。它使代码更难读将它移动到它自己的组件中,命名它并传递props,而不是以闭包的形式存在。

// bad 不要将其定义在渲染函数组件中
function Component() {
  function renderHeader() {
    return <header>...</header>
  }
  return <div>{renderHeader()}</div>
}

// good 将其抽离到独立的组件中去
import Header from '@modules/common/components/Header'

function Component() {
  return (
    <div>
      <Header />
    </div>
  )
}
  1. 必须】避免嵌套三元运算符
// bad 不够清晰,要是再嵌套一层两层呢
isSubscribed ? (
  <ArticleRecommendations />
) : isRegistered ? (
  <SubscribeCallToAction />
) : (
  <RegisterCallToAction />
)

// good 将判断逻辑进行拆分
function CallToActionWidget({ subscribed, registered }) {
  if (subscribed) {
    return <ArticleRecommendations />
  }

  if (registered) {
    return <SubscribeCallToAction />
  }

  return <RegisterCallToAction />
}

function Component() {
  return (
    <CallToActionWidget
      subscribed={subscribed}
      registered={registered}
    />
  )
}
  1. 【推荐】 在jsx Props中禁用箭头函数函数 eslint: [react/jsx-no-bind](<https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md>)
// bad
<Foo onClick={() => console.log('Hello!')}></Foo>
  1. **【推荐】 **以正确的顺序创建函数组件

建议先使用 useState Hook 声明状态变量,然后使用 useEffect Hook 编写订阅,接着编写与组件业务相关的其他函数。最后,你得返回要由浏览器渲染的元素:

function App() {
  const [user, setUser] = useState(null);
  const [name, setName] = useState('');
  useEffect(() => {
    console.log("component is mounted");
  }, []);
  return <h1>React component order</h1>;
}
  1. 【推荐】 状态提升

如果多个组件需要反映相同的变化数据,建议将共享状态提升到最近的共同父组件中去;从而依靠自上而下的数据流,而不是尝试在不同组件间同步 state。

  1. 【推荐】 props传参数量不超过5个

如果超过 5 个props,就该考虑是否拆分该组件,props越多 重新渲染的原因也就越多

  1. 【推荐】 解构Props
// bad 不要在组件中到处重复传递props
function Input(props) {
  return <input value={props.value} onChange={props.onChange} {...props}/>
}

// good 对props解构并使用这些值
function Component({ value, onChange }) {
  const [state, setState] = useState('')

  return <div>...</div>
}

18.**【推荐】**传递对象而不是基础数据

// bad 如果值都是相关的,不用一个一个传递
<UserProfile
  bio={user.bio}
  name={user.name}
  email={user.email}
  subscription={user.subscription}
/>

// good 使用对象进行传递,在后期增改字段也更方便
<UserProfile user={user} />
  1. 【推荐】使用布尔型props的速记
// bad
<RegistrationForm hasPadding={true} withError={true} />

// good
<RegistrationForm hasPadding withError />
  1. 【必须】当你想使用一个字符串时,避免使用大括号;当使用JavaScript表达式时,才使用大括号

当你想使用一个不同于字符串的JavaScript表达式时,你需要使用大括号

// bad
<Paragraph variant={"h5"} heading={"A new book"} />

// good
<Paragraph variant="h5" heading="A new book" />
  1. **【推荐】**惰性初始state

需要复杂计算的初始state 避免在render过程中被频繁计算

//bad
const initalState = heavyCompute(() => { /* do some heavy compute here*/});
const [state,useState] = useState(initalState);

// good
// 这样初始值就只会被计算一次了
const [state,useState] = useState(() => heavyCompute(() => { /* do some heavy compute here*/}););
  1. 【推荐】 避免state颗粒度过细,
// bad
const {name,age,height} = data
setName(name)
setAge(age)
setHeight(height)

//good
const {name,age,height} = data
setUser({name,age,height})
  1. 【必须】 避免无意义的state

对于可以直接复用props 的场景不需要在子组件内维护state

// bad
function FatherComponent() {
	const [name, setName] = useState('andy')
  return <SonComponent name={name}/>
}

// good 对props解构并使用这些值
function SonComponent({ name}) {
  const [sonName, setSonName] = useState(()=>name)

  return <div>{sonName}</div>
}
  1. 【必须】在 componentWillUnmount 里面去除副作用的函数
  • 清除 EventListener
  • 中止数据请求
  • 清除定时器
  1. **【推荐】**正确的使用useCallback

在大多数情况下,javascript 创建一个函数的开销是很小的,哪怕每次渲染都重新创建。真正的性能损耗在于,很多时候 callback 函数是组件 props 的一部分,维持函数引用,在只需要维持函数引用的情况下推荐使用 useCallback,当需要借助依赖维持引用推荐使用 useMemoizedFn。

 A  const [count, setCount] = useState(0);

  const callbackFn = useCallback(() => {
   console.log('123')
  }, []);
  const callbackFnDep = useCallback(() => {
   console.log('count',count)
  }, [count]);

  const callbackFnDep = useCallback(() => {
   console.log('count',count)
  }, [count]);

  const memoizedFn = useMemoizedFn(() => {
    message.info(`Current count is ${count}`);
  });

  return (
    <>
        <ExpensiveTree showCount={callbackFn} />
        // bad
        <ExpensiveTree showCount={callbackFnDep} />
        // good
        <ExpensiveTree showCount={memoizedFn} />
    </>
  );
};
  1. 【推荐】避免滥用 useMemo

滥用 useMemo 会带来性能损耗,推荐在需要缓存复杂计算值的场景下使用useMemo

//bad
const Comp = () => {
    const data = useMemo(() => ({ type: 'xxx' }), []);
    return <Child data={data}>;
}

// good
const Comp = () => {
    const { current: data } = useRef({ type: 'xxx' });
    return <Child data={data}>;
}

// good
const data = { type: 'xxx' };
const Comp = () => {
    return <Child data={data}>;
}
// good
const Comp = () => {
    const data = useMemo(() => { //复杂计算 }, []);
    return <Child data={data}>;
}
  1. 【必须】将可重复使用的逻辑提取到自定义hook
const useWindowSize = () => {
  const [windowSize, setWindowSize] = useState({ju
    width: undefined,
    height: undefined,
  });
  
  useEffect(() => {
    function handleResize() {
      setWindowSize({
        width: window.innerWidth,
        height: window.innerHeight,
      });
    }
    window.addEventListener('resize', handleResize);
    handleResize();
    return () => window.removeEventListener('resize', handleResize);
  }, []);
  
  return windowSize;
}

const ScreenDimensions = () => {
  const windowSize = useWindowSize()
  
  return (
  	<>
    	<p>Current screen width: {windowSize.width}</p>
        <p>Current screen height: {windowSize.height}</p>
    </>
  )
}
  1. 【必须】多语言字符串,不要随意将两个key对应的字符串拼接使用

不同语言的语言习惯和语序不同,基于中文拼接出的词组,在其他语言中可能就错了

// bad
placeholder
          ? `${t('common.search')}${placeholder}`
          : placeholder || t('common.pleaseEnterContent')
  1. 【推荐】避免组件嵌套过深

组件一般不要超过三层,最多四层,层级过深可能会导致数据传递过深,在做一些颗粒度比较细的操作的时候,处理起来较为繁琐,可以使用 redux 等状态管理工具替代。