-
使用函数式组件:函数式组件是 React 中推荐的编写组件的方式,相对于类组件,它们具有更轻量的语法和更好的性能表现。
-
使用 React.memo 进行组件的浅比较优化:使用 React.memo 可以包裹组件,在传入的 props 发生变化时,进行浅比较,避免不必要的重渲染。
import React from 'react';
interface Props {
name: string;
}
const MyComponent: React.FC<Props> = React.memo(({ name }) => {
return <div>{name}</div>;
});
- 使用 useCallback 优化回调函数:使用 useCallback 来对回调函数进行缓存,以确保在父组件重新渲染时,回调函数的引用保持不变,避免不必要的子组件重渲染。
import React, { useCallback } from 'react';
interface Props {
onClick: () => void;
}
const MyButton: React.FC<Props> = ({ onClick }) => {
const handleClick = useCallback(() => {
onClick();
}, [onClick]);
return <button onClick={handleClick}>Click Me</button>;
};
- 使用 useMemo 优化计算结果:使用 useMemo 来对计算结果进行缓存,以避免在每次渲染时都重新计算相同的结果。
import React, { useMemo } from 'react';
interface Props {
a: number;
b: number;
}
const MyComponent: React.FC<Props> = ({ a, b }) => {
const sum = useMemo(() => {
console.log('Calculating sum');
return a + b;
}, [a, b]);
return <div>{sum}</div>;
};
- 避免在 render 方法中定义新对象:在 render 方法中定义新的对象,会导致在每次重新渲染时都创建新的对象实例,建议将对象定义在 render 方法外部。
import React from 'react';
interface Props {
data: number[];
}
const MyComponent: React.FC<Props> = ({ data }) => {
const mappedData = data.map((item) => item * 2);
return (
<div>
{mappedData.map((item) => (
<span key={item}>{item}</span>
))}
</div>
);
};
- 类型推断和类型声明:合理利用 TypeScript 的类型推断和类型声明,增加代码的可读性和可维护性,避免潜在的类型错误。
7. 类型推断:
const name = 'John'; // TypeScript会自动推断name的类型为string
const age = 25; // TypeScript会自动推断age的类型为number
function multiply(a: number, b: number) {
return a * b;
}
const result = multiply(5, 10); // TypeScript会自动推断result的类型为number
8. 类型声明:
interface Person {
name: string;
age: number;
}
function greet(person: Person) {
console.log(`Hello, ${person.name}! You are ${person.age} years old.`);
}
const john: Person = { // 使用类型声明明确john的类型为Person
name: 'John',
age: 25,
};
greet(john);
9. 类型推断和类型声明结合使用:
function add(a: number, b: number): number {
return a + b;
}
const result = add(5, 10); // TypeScript根据add函数的返回类型声明result的类型为number
const user = {
name: 'John',
age: 25,
};
type User = typeof user; // 使用typeof关键字将user对象的类型推断为User类型
const newUser: User = {
name: 'Jane',
age: 30,
};
- 使用 interface 或 type 声明组件 props 和状态:使用接口(interface)或类型(type)来明确定义组件的 props 和状态的类型,提高代码的可读性和可维护性。
1. 使用接口定义组件的props类型:
interface ButtonProps {
onClick: () => void;
disabled?: boolean;
label: string;
}
const Button: React.FC<ButtonProps> = ({ onClick, disabled, label }) => {
return (
<button onClick={onClick} disabled={disabled}>
{label}
</button>
);
};
2. 使用类型别名定义组件的props类型:
type ButtonProps = {
onClick: () => void;
disabled?: boolean;
label: string;
};
const Button: React.FC<ButtonProps> = ({ onClick, disabled, label }) => {
return (
<button onClick={onClick} disabled={disabled}>
{label}
</button>
);
};
3.使用接口定义组件的状态类型:
interface CounterState {
count: number;
step: number;
}
class Counter extends React.Component<{}, CounterState> {
state: CounterState = {
count: 0,
step: 1,
};
render() {
const { count, step } = this.state;
return (
<div>
<p>Count: {count}</p>
<button onClick={this.increment}>Increment</button>
</div>
);
}
increment = () => {
this.setState((prevState) => ({
count: prevState.count + this.state.step,
}));
};
}
4. 使用类型别名定义组件的状态类型:
type CounterState = {
count: number;
step: number;
};
class Counter extends React.Component<{}, CounterState> {
state: CounterState = {
count: 0,
step: 1,
};
render() {
const { count, step } = this.state;
return (
<div>
<p>Count: {count}</p>
<button onClick={this.increment}>Increment</button>
</div>
);
}
increment = () => {
this.setState((prevState) => ({
count: prevState.count + this.state.step,
}));
};
}
- 使用枚举类型:使用 TypeScript 的枚举类型来增加代码的可读性,避免魔法数字。
enum Direction {
Up = 'UP',
Down = 'DOWN',
Left = 'LEFT',
Right = 'RIGHT',
}
function move(direction: Direction): void {
if (direction === Direction.Up) {
console.log('Moving up');
} else if (direction === Direction.Down) {
console.log('Moving down');
} else if (direction === Direction.Left) {
console.log('Moving left');
} else if (direction === Direction.Right) {
console.log('Moving right');
}
}
move(Direction.Up); // 输出:Moving up
move(Direction.Left); // 输出:Moving left
使用魔法数字的弊端:
1.可读性差:直接在代码中使用数字,特别是没有注释或解释的情况下,会使代码变得难以理解和维护。其他开发人员阅读代码时可能无法立即理解数字的含义和目的。
2.可维护性差:当需要修改代码时,如果魔法数字散布在整个代码库中,就需要逐个查找并修改每个实例。这增加了出错的风险,并且容易遗漏或忽略一些实例。
3.难于调试:如果出现错误或需要调试代码时,使用魔法数字会使问题的定位和排查变得困难。调试器和日志消息通常不会提供有关具体数字的上下文信息。
4.可复用性差:如果将来需要在其他地方使用相同的值,或者需要根据需求进行调整,重新使用魔法数字的代码会变得困难并且容易引入错误。
- 使用可选链操作符(Optional Chaining):通过使用可选链操作符(?.)来访问可能为空的属性或方法,在代码中减少了一些不必要的空值判断。
可选链操作符(Optional Chaining)的规则如下:
1.对象属性的可选链操作:使用?.来访问对象的属性。如果属性存在且不为null或undefined,表达式将继续求值;如果属性不存在或为null或undefined,表达式将立即返回undefined。
const obj = {
prop1: {
prop2: {
prop3: 'value',
},
},
};
console.log(obj.prop1?.prop2?.prop3); // 输出:'value'
console.log(obj.prop1?.prop2?.prop4); // 输出:undefined
2. 函数调用的可选链操作:使用?.来调用可能为null或undefined的函数。如果函数存在且不为null或undefined,调用将正常执行;如果函数不存在或为null或undefined,调用将不会引发错误,而是立即返回undefined。
const obj = {
func: () => 'Hello',
};
console.log(obj.func?.()); // 输出:'Hello'
console.log(obj.unknownFunc?.()); // 输出:undefined
注意:可选链操作符在部分浏览器和环境中可能还不被完全支持,请根据目标环境进行兼容性考虑或使用工具进行转译。
- 合理拆分和组织组件:将大型组件拆分为更小的组件,提高代码的可维护性和重用性。同时,通过合理组织组件的文件结构,使代码更易于理解和维护。