引言
对于React组件类型, 我相信大家一定有一些疑惑和不清晰的地方。而官方文档和网上的教程大多仅告诉我们如何定义/使用组件, 其中组件的差异和选用的理由并没有细说。本文将介绍React中常见的组件类型,以及它们的特点和使用场景。
React组件类型有哪些?
在React中,组件类型分为两种:函数式组件和类组件,具体可以细分为以下几种:
- Function Component(函数组件):这是最简单的组件类型,它只是一个接收 props 作为参数并返回 React 元素的 JavaScript 函数。
function Greeting(props) {
return <h1>Hello, {props.name}!</h1>;
}
这边介绍另一种函数式组件的写法:
type Props = { name: string };
const Greeting: React.FC<Props> = ({ name }) => {
return <div>Hello {name}</div>;
}
React.FC 是一个泛型接口,它定义了一个 React 函数组件的类型,这个类型指定了组件接收的 props 类型和组件返回的元素类型。使用 React.FC 可以让我们更方便地定义函数组件,并且 TypeScript 可以在编译时检查我们的组件是否符合类型定义。
- Class Component(类组件):这是使用 ES6 类语法定义的组件类型,它可以拥有自己的状态和生命周期方法。
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
handleClick = () => {
this.setState({ count: this.state.count + 1 });
};
render() {
return (
<div>
<p>Count: {this.state.count}</p>
<button onClick={this.handleClick}>Increment</button>
</div>
);
}
}
- PureComponent(纯组件):这是一个优化过的类组件,它可以浅比较 props 和 state 的变化,以决定是否需要重新渲染组件。
class Person extends React.PureComponent {
render() {
return <p>Name: {this.props.name}</p>;
}
}
- Memo Component(记忆组件):这是一个优化过的函数组件,它可以浅比较 props 的变化,以决定是否需要重新渲染组件。
const MemoizedPerson = React.memo(Person);
- Higher-Order Component(高阶组件):这是一个函数,它接收一个组件作为参数并返回一个新的组件。高阶组件可以用来封装公共的逻辑,例如用于授权、日志记录、数据预处理等。
function withLogger(WrappedComponent) {
return class extends React.Component {
componentDidMount() {
console.log(`Component ${WrappedComponent.name} mounted.`);
}
componentWillUnmount() {
console.log(`Component ${WrappedComponent.name} unmounted.`);
}
render() {
return <WrappedComponent {...this.props} />;
}
};
}
const LoggedCounter = withLogger(Counter);
- Render Prop Component(渲染属性组件):这是一个使用 render props 模式实现的组件类型,它通过 props 传递一个函数给子组件,以控制子组件的渲染逻辑。
class MouseTracker extends React.Component {
state = { x: 0, y: 0 };
handleMouseMove = (event) => {
this.setState({ x: event.clientX, y: event.clientY });
};
render() {
return this.props.render(this.state);
}
}
const App = () => (
<MouseTracker render={(mouse) => (
<p>The mouse position is {mouse.x}, {mouse.y}.</p>
)} />
);
- Hooks(钩子函数):这是 React 16.8 引入的新特性,它可以让函数组件拥有自己的状态和生命周期方法,以及实现其他功能,例如使用上下文、访问 DOM 元素等。
import { useState, useEffect } from 'react'
function Counter() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
};
useEffect(() => {
console.log(`Count: ${count}`);
}, [count]);
return (
<div>
<p>Count: {count}</p>
<button onClick={handleClick}>Increment</button>
</div>
);
}
变种组件的使用场景
PureComponent(纯组件)
使用 PureComponent 可以避免不必要的重新渲染。
这句话比较抽象, 我们举一个场景来说明:
假设我们有一个展示商品信息的组件,它的props包含商品名称、价格、描述等信息,其中价格是一个复杂的对象,包含原价、折扣价、折扣信息等属性。在这种情况下,如果我们使用普通的函数组件来实现这个展示组件,每次父组件重新渲染时,即使商品价格没有改变,由于价格对象的引用地址发生了变化,组件也会重新渲染,造成性能浪费。
import React, { PureComponent } from 'react';
class ProductInfo extends PureComponent {
render() {
const { name, price, description } = this.props;
return (
<div>
<h2>{name}</h2>
<p>{description}</p>
<h3>Price</h3>
<p>Original Price: {price.originalPrice}</p>
<p>Discounted Price: {price.discountedPrice}</p>
<p>Discount Info: {price.discountInfo}</p>
</div>
);
}
}
export default ProductInfo;
在上面的代码中,我们使用了 PureComponent 来定义展示商品信息的组件。在 PureComponent 中,React 会自动进行浅比较(shallow comparison)来判断是否需要重新渲染组件,如果 props 和 state 的引用地址没有变化,就不会重新渲染组件,从而避免了不必要的性能开销。
Memo Component(记忆组件)
Memo Component(记忆组件)适用于在父组件中传递给子组件的 props 只有部分属性发生变化,而子组件仅仅依赖于这些变化的属性。这种情况下,Memo Component 可以帮助避免不必要的重新渲染,提高应用性能。
假设有一个包含多个联系人的列表,用户可以根据名字或邮箱地址搜索联系人。列表项组件将会根据传入的联系人数据进行展示,并且只有在搜索条件改变时才会重新渲染:
import React, { memo } from 'react';
// 列表项组件
const ContactListItem = memo(({ contact }) => {
console.log('Rendering ContactListItem', contact.name);
return (
<div className="contact">
<h2>{contact.name}</h2>
<p>{contact.email}</p>
</div>
);
});
// 列表组件
const ContactList = ({ contacts, search }) => {
console.log('Rendering ContactList');
const filteredContacts = contacts.filter(contact =>
contact.name.includes(search) || contact.email.includes(search)
);
return (
<div className="contact-list">
{filteredContacts.map(contact => (
<ContactListItem key={contact.id} contact={contact} />
))}
</div>
);
};
export default ContactList;
在上面的代码中,我们使用了 memo 函数来包装 ContactListItem 组件,这样它就变成了一个记忆组件。当 ContactList 组件的 contacts 或 search 属性发生改变时,只有被 memo 包装的 ContactListItem 组件会重新渲染,而不是每个列表项都重新渲染。这样可以显著提高应用性能,特别是在列表数据较多的情况下。
总结
React提供了多种类型的组件,让我们可以根据不同的场景和需求选择合适的组件。本文介绍了React中常见的组件类型,以及它们的特点和使用场景。希望本文能够帮助大家更好地理解和使用React。