# 如何声明React组件 ## 前言 在现代前端开发中,React作为最流行的JavaScript库之一,其组件化思想彻底改变了我们构建用户界面的方式。声明组件是React开发的基础,也是每个React开发者必须掌握的核心技能。本文将全面介绍React中声明组件的各种方式、最佳实践以及相关高级技巧,帮助您构建更健壮、更易维护的React应用。 ## 一、React组件基础概念 ### 1.1 什么是React组件 React组件是构建用户界面的独立、可复用的代码单元。每个组件都封装了自己的结构(HTML)、样式(CSS)和行为(JavaScript),可以像搭积木一样组合起来构建复杂的UI。 ### 1.2 组件的重要性 - **模块化**:将UI分解为独立可管理的部分 - **可复用性**:一次编写,多处使用 - **可维护性**:隔离的代码更易于理解和修改 - **单一职责**:每个组件只关注一个特定功能 ### 1.3 组件类型概述 React主要支持两种组件声明方式: 1. 函数组件(Functional Components) 2. 类组件(Class Components) 随着React Hooks的引入,函数组件已成为主流选择,但理解类组件仍然重要,特别是在维护老代码库时。 ## 二、函数组件声明 ### 2.1 基本函数组件 最简单的函数组件就是一个返回JSX的JavaScript函数: ```jsx function Welcome(props) { return <h1>Hello, {props.name}</h1>; }
或者使用箭头函数:
const Welcome = (props) => { return <h1>Hello, {props.name}</h1>; };
const Welcome = ({ name }) => { return <h1>Hello, {name}</h1>; };
const Welcome = ({ name = 'Guest' }) => { return <h1>Hello, {name}</h1>; };
或者使用静态属性:
function Welcome({ name }) { return <h1>Hello, {name}</h1>; } Welcome.defaultProps = { name: 'Guest' };
const Card = ({ children }) => { return <div className="card">{children}</div>; }; // 使用 <Card> <h2>Title</h2> <p>Content</p> </Card>
class Welcome extends React.Component { render() { return <h1>Hello, {this.props.name}</h1>; } }
class Counter extends React.Component { constructor(props) { super(props); this.state = { count: 0 }; } render() { return ( <div> <p>Count: {this.state.count}</p> <button onClick={() => this.setState({ count: this.state.count + 1 })}> Increment </button> </div> ); } }
class UserProfile extends React.Component { componentDidMount() { // 组件挂载后执行 this.fetchUserData(); } componentDidUpdate(prevProps) { // props更新后执行 if (this.props.userId !== prevProps.userId) { this.fetchUserData(); } } componentWillUnmount() { // 组件卸载前执行 this.abortController.abort(); } fetchUserData() { // 获取用户数据 } render() { // 渲染UI } }
class Welcome extends React.Component { static defaultProps = { name: 'Guest' }; render() { return <h1>Hello, {this.props.name}</h1>; } }
import { useState } from 'react'; function Counter() { const [count, setCount] = useState(0); return ( <div> <p>Count: {count}</p> <button onClick={() => setCount(count + 1)}>Increment</button> </div> ); }
import { useState, useEffect } from 'react'; function UserProfile({ userId }) { const [user, setUser] = useState(null); useEffect(() => { const fetchData = async () => { const response = await fetch(`/api/users/${userId}`); const data = await response.json(); setUser(data); }; fetchData(); }, [userId]); // 依赖数组,userId变化时重新执行 if (!user) return <div>Loading...</div>; return <div>{user.name}</div>; }
function useFetch(url) { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { const fetchData = async () => { try { const response = await fetch(url); const result = await response.json(); setData(result); } catch (err) { setError(err); } finally { setLoading(false); } }; fetchData(); }, [url]); return { data, loading, error }; } // 使用自定义Hook function UserProfile({ userId }) { const { data: user, loading, error } = useFetch(`/api/users/${userId}`); if (loading) return <div>Loading...</div>; if (error) return <div>Error: {error.message}</div>; return <div>{user.name}</div>; }
function withLogger(WrappedComponent) { return function(props) { useEffect(() => { console.log(`${WrappedComponent.name} mounted`); return () => { console.log(`${WrappedComponent.name} unmounted`); }; }, []); return <WrappedComponent {...props} />; }; } // 使用高阶组件 const EnhancedComponent = withLogger(MyComponent);
class MouseTracker extends React.Component { state = { x: 0, y: 0 }; handleMouseMove = (event) => { this.setState({ x: event.clientX, y: event.clientY }); }; render() { return ( <div onMouseMove={this.handleMouseMove}> {this.props.render(this.state)} </div> ); } } // 使用 <MouseTracker render={({ x, y }) => ( <h1>The mouse position is ({x}, {y})</h1> )} />
const Tabs = ({ children }) => { const [activeTab, setActiveTab] = useState(0); return ( <div className="tabs"> <div className="tab-headers"> {React.Children.map(children, (child, index) => ( <button onClick={() => setActiveTab(index)} className={index === activeTab ? 'active' : ''} > {child.props.title} </button> ))} </div> <div className="tab-content"> {React.Children.toArray(children)[activeTab]} </div> </div> ); }; const Tab = ({ children }) => { return <div>{children}</div>; }; // 使用 <Tabs> <Tab title="First">Content 1</Tab> <Tab title="Second">Content 2</Tab> <Tab title="Third">Content 3</Tab> </Tabs>
const MyComponent = React.memo(function MyComponent(props) { /* 只有当props改变时才会重新渲染 */ return <div>{props.value}</div>; });
const MemoizedButton = React.memo(function Button({ onClick, children }) { return <button onClick={onClick}>{children}</button>; }); function Parent() { const [count, setCount] = useState(0); const increment = useCallback(() => { setCount(c => c + 1); }, []); // 依赖数组为空,函数不会重新创建 return ( <div> <p>Count: {count}</p> <MemoizedButton onClick={increment}>Increment</MemoizedButton> </div> ); }
function ExpensiveComponent({ items, filter }) { const filteredItems = useMemo(() => { return items.filter(item => item.includes(filter)); }, [items, filter]); // 只有当items或filter变化时才重新计算 return ( <ul> {filteredItems.map(item => ( <li key={item}>{item}</li> ))} </ul> ); }
interface WelcomeProps { name: string; age?: number; // 可选属性 } const Welcome: React.FC<WelcomeProps> = ({ name, age = 18 }) => { return ( <div> <h1>Hello, {name}</h1> <p>Age: {age}</p> </div> ); };
interface CounterProps { initialCount?: number; } interface CounterState { count: number; } class Counter extends React.Component<CounterProps, CounterState> { state: CounterState = { count: this.props.initialCount || 0 }; increment = () => { this.setState(prevState => ({ count: prevState.count + 1 })); }; render() { return ( <div> <p>Count: {this.state.count}</p> <button onClick={this.increment}>Increment</button> </div> ); } }
每个组件应该只负责一个功能。如果一个组件变得过于复杂,应该考虑将其拆分为更小的子组件。
推荐的文件结构:
components/ Button/ index.tsx // 组件代码 styles.module.css // 组件样式 types.ts // 类型定义 index.stories.tsx // Storybook故事 test.tsx // 测试文件
可能原因: - 状态直接修改而不是使用setState/useState - 错误地使用了React.memo或useMemo - 依赖数组未正确设置
常见场景: - 未取消订阅事件 - 未清除定时器 - 未中止fetch请求
解决方案:
useEffect(() => { const controller = new AbortController(); fetch(url, { signal: controller.signal }) .then(/* ... */) .catch(/* ... */); return () => { controller.abort(); // 清除操作 }; }, [url]);
React 18引入的服务器组件(Server Components)将改变我们声明组件的方式:
// 服务器组件 async function UserProfile({ userId }) { const user = await db.users.get(userId); // 直接在服务器端执行 return ( <div> <h1>{user.name}</h1> <UserPosts userId={userId} /> </div> ); } // 客户端组件 'use client'; function UserPosts({ userId }) { const [posts, setPosts] = useState([]); useEffect(() => { fetchPosts(userId).then(setPosts); }, [userId]); return ( <ul> {posts.map(post => ( <li key={post.id}>{post.title}</li> ))} </ul> ); }
React团队正在开发的”React Forget”编译器可能会自动优化组件,减少手动记忆化(memoization)的需要。
声明React组件看似简单,实则包含了许多细节和最佳实践。随着React生态系统的不断发展,组件声明方式也在不断演进。掌握各种组件声明方式及其适用场景,将帮助您构建更高效、更易维护的React应用。记住,好的组件设计是React应用成功的关键。
无论您是React新手还是经验丰富的开发者,都应该持续关注React的最新发展,并不断优化您的组件声明方式。Happy coding! “`
这篇文章涵盖了React组件声明的各个方面,从基础到高级技巧,总字数约3850字。内容按照逻辑顺序组织,包含了代码示例、最佳实践和未来趋势,适合不同水平的React开发者阅读学习。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。