面向新手的 React 实战经验

958 阅读5分钟

前言

这篇文章的内容基于自己日常开发经验,主要是一些实战小技巧和踩坑心得,希望能对大家有所帮助。持续更新~

注释

养成一个写注释的好习惯利人利己,写注释时要站在同事的角度,简明扼要:

  • 单行注释
// 程序描述
function toStr(v) { // 块描述
    // 代码段描述
    return v.toString(); // 语句描述
}
  • 多行注释
/** 
* 在多行注释中,
* 包含在 /** 和 */ 符号之间的任何字符,
* 都会被视为注释文本
*/
  • 注释前缀
// TODO: 接口等待联调
// FIXME: 列表更新有bug,待修复

引入模块顺序

  • 第三方的放最上面
  • 其次是当前项目的其它模块
  • 最后是 CSS 样式
import * as React from 'react;
import {Button, Input} from 'antd';
import Header from './Header';
import {Status} from './const';
import * as styles from './index.less';

代码风格

以下是主流的编码风格,具体还得遵循团队规范:

  • 使用单引号,或者 es6 的反引号
  • 除了代码块以外的每个表达式,结尾必须加分号
  • 变量和函数采用小驼峰命名
  • 常量用大写字母命名,单词之间用下划线分隔,例如:STATUS_MAP
  • 组件内的事件函数使用 handle 开头,例如:handleConfirm
  • 通过 porps 传递函数时使用 onXxx 的形式作为回调函数的属性名称
const App = () => {
    const handleCancel = () => {
        ...
    }
    
    return (
        <div>
            Hello !
            <Child onClose={handleCancel}/>
        </div>
    )
}
  • 不要过分省略大括号
// not good
if(condition) doSomething();

// good
if (condition) {
    doSomething();
}

语义化

给状态和函数命名时要做到语义化,让人看完英语单词立即能够知道这个状态代表哪个值、这个函数是处理什么事件,要尽量避免未知含义的代码:

// not good 
const [value, setValue] = useState();

// good
const [checkedValue,setCheckedValue] = useState();
// not good
const handleClick = () => {}

// good
const handleConfirm = () => {}
// bad(同事一脸懵逼)
if (type !== 0) {
    // TODO
}

// good
const STATUS = {
    FAILED: 0,
    SUCCESS: 1,
}

if (type === STATUS.SUCCESS) {
    // TODO
}

// best(TS枚举)
enum STATUS {
    FAILED = 0,
    SUCCESS = 1,
}

渲染默认值

添加非空判断,利用默认值进行预防,可以提高代码的稳健性。例如后端返回的一些值,可能会出现不存在的情况,这时候给一个默认值就能避免很多缺陷:

const res = request();

if (res && res.success) {
    message.success('删除成功');
} else {
    // 如果后端没有暴露出失败的具体原因,就提示 ‘删除失败’
    message.error(res.message || '删除失败');
}

还有一种情况,后端本来应该返回一个数组,但是数据库里取不到数据,这时可能返回 null。如果代码里有将 list.length作为条件判断,就会报错。最简单的解决方法就是添加非空判断和默认值:

const [listData, setListData] = useState([]);

const { data: { list } } = request();
// 如果 list 为 null,就会取 [],下面的 listData.length 就不会报错
setListData(list || []) 

if (listData.length > 0) {
   ......
}

数据格式转换

在开发时一定要多留意后端返回的数据类型是不是符合我们的预期,有时候很多问题都是由此导致的。

例如,我们拿到的idstring类型,但是在调其它接口传参数时需要传number类型,这很容易被忽略。以下两种将字符串转换成数字的方法是等价的:

 const a = '2022';
 
 console.log(+a); // 2022(number) 最简单
 console.log(Number(a)); // 2022(number)

还有一种情况需要注意,后端返回的 ‘布尔值’,其实是个字符串类型:‘false’‘true’。如果我们直接用来判断,则永远为true。曾经因为这个问题导致的一个bug差点给我整崩溃了~

转成布尔值推荐使用 双重逻辑非!!

console.log(!!0); // false
console.log(!!1); // true
console.log(!!""); // false
console.log(!!null); // false
console.log(!!undefined); // false
console.log(!![]); // true
console.log(!!{}); // true

判断条件真假

以下为假,其它为真:

  • false
  • null
  • undefined
  • 0
  • '' (空字符串)
  • NaN

解构赋值

写代码尽量简洁,这样更易于阅读和后期维护。使用解构赋值将频繁使用的属性或值先进行缓存,后面使用的话会更加方便:

// bad
props.data.id;
props.data.username;
state.detail.status;

// good
const { id, username } = props.data;
const { status } = state.detail;

input 输入框去空格

// bad
const handleInputChange = (e) => {
    setInputValue(e.target.value);
}

// good
const handleInputChange = (e) => {
    setInputValue(e.target.value.trim());
}

try catch

发送 ajax 请求时,使用try catch捕获错误,也可以根据返回的状态进行一些操作。例如,进入列表页时首先需要一个loading状态,然后去请求数据,无论请求成功还是失败,都需要把loading去掉:

const getList = async () => {
    try {
        setLoading(true);
        const res = await request();
    } catch (err) {
        // TODO
    } finally {
        setLoading(false);
    }
}

阻止事件默认行为

在 React 中不能通过返回 false 来阻止默认行为,必须明确调用 event.preventDefault()

阻止冒泡

event.stopPropagation()

减少 useEffect 依赖

useEffect依赖越多,复杂度就越高,容易导致 bug:

// bad
function Component({ id, name }) {
    useEffect(() => {
        setId(id);
        setName(name);
    }, [id, name, ...更多]);
}

// good
function Component({ id, name }) {
    useEffect(() => {
        setId(id);
    }, [id]);
    
    useEffect(() => {
        setName(name);
    }, [name]);
}

key

key只是在兄弟节点之间必须唯一,并不需要全局唯一。避免使用indexdate作为 key 值。如果实在取不到唯一值,可以使用nanoid这个库来生成 uuid (通用唯一识别码):

npm i nanoid

import { nanoid } from 'nanoid'

// 直接调用,即可生成一个 uuid
key={ nanoid() }

组件实例是基于它们的key来决定是否更新以及复用,如果key是一个下标,那么修改顺序时index也会随之改变,即当前key发生了变化,这可能会导致非受控组件的 state(比如输入框) 相互篡改,发生无法预期的变动。

key应该具有唯一性稳定性可预测性。不稳定的 key(比如new Date()、Math.random()) 会导致许多组件实例和DOM节点被不必要地重新创建,这可能导致性能下降和子组件中的状态丢失。

最后

如果文中有错误或者不足之处,欢迎大家在评论区指正。

你的点赞是对我莫大的鼓励!感谢阅读~