组件结构
// 使用函数组件和 Hooks
const MyComponent = ({ prop1, prop2 }) => {const [state, setState] = useState(initialState);useEffect(() => {// 副作用逻辑}, [dependencies]);return (<div>{/* JSX 结构 */}</div>);
};
代码分割和懒加载
const LazyComponent = React.lazy(() => import('./LazyComponent'));function App() {return (<Suspense fallback={<div>Loading...</div>}><LazyComponent /></Suspense>);
}
性能优化
// 使用 useMemo 缓存计算结果
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);// 使用 useCallback 缓存函数
const memoizedCallback = useCallback(() => doSomething(a, b), [a, b]);// 使用 React.memo 避免不必要的重渲染
const MemoizedComponent = React.memo(MyComponent);
状态管理
// 使用 useReducer 管理复杂状态
const [state, dispatch] = useReducer(reducer, initialState);// 使用 Context API 进行全局状态管理
const MyContext = React.createContext(defaultValue);
类型检查(使用 TypeScript 或 PropTypes)
interface Props {name: string;age: number;
}const MyComponent: React.FC<Props> = ({ name, age }) => {// ...
};
错误边界
class ErrorBoundary extends React.Component {// ...错误处理逻辑
}<ErrorBoundary><MyComponent />
</ErrorBoundary>
测试
import { render, fireEvent } from '@testing-library/react';test('component renders correctly', () => {const { getByText } = render(<MyComponent />);expect(getByText('Hello')).toBeInTheDocument();
});
代码风格和最佳实践
使用 ESLint 和 Prettier 保持代码风格一致
遵循 React 的最佳实践,如使用 key 属性、避免过度使用 state 等
测试
测试类型
a. 单元测试:测试独立的函数或组件
b. 集成测试:测试多个组件的交互
c. 端到端测试:模拟用户行为,测试整个应用流程
常用测试工具
Jest:JavaScript 测试框架
React Testing Library:用于测试 React 组件
Enzyme:Airbnb 的 React 测试工具(虽然现在更推荐 React Testing Library)
测试示例
```javascript
// MyComponent.tsx
import React from 'react';interface Props {name: string;
}const MyComponent: React.FC<Props> = ({ name }) => {return <div>Hello, {name}!</div>;
};export default MyComponent;// MyComponent.test.tsx
import React from 'react';
import { render, screen } from '@testing-library/react';
import MyComponent from './MyComponent';describe('MyComponent', () => {test('renders greeting with name', () => {render(<MyComponent name="Alice" />);expect(screen.getByText('Hello, Alice!')).toBeInTheDocument();});
});
测试异步操作
// AsyncComponent.tsx
import React, { useState, useEffect } from 'react';const AsyncComponent = () => {const [data, setData] = useState<string | null>(null);useEffect(() => {const fetchData = async () => {const response = await fetch('https://api.example.com/data');const result = await response.json();setData(result.message);};fetchData();}, []);return <div>{data ? data : 'Loading...'}</div>;
};// AsyncComponent.test.tsx
import React from 'react';
import { render, screen, waitFor } from '@testing-library/react';
import AsyncComponent from './AsyncComponent';jest.mock('node-fetch');describe('AsyncComponent', () => {test('renders data after fetch', async () => {const mockFetch = jest.fn().mockResolvedValue({json: () => Promise.resolve({ message: 'Hello from API' }),});global.fetch = mockFetch;render(<AsyncComponent />);expect(screen.getByText('Loading...')).toBeInTheDocument();await waitFor(() => {expect(screen.getByText('Hello from API')).toBeInTheDocument();});});
});
测试用户交互
// Counter.tsx
import React, { useState } from 'react';const Counter = () => {const [count, setCount] = useState(0);return (<div><p>Count: {count}</p><button onClick={() => setCount(count + 1)}>Increment</button></div>);
};// Counter.test.tsx
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import Counter from './Counter';describe('Counter', () => {test('increments count when button is clicked', () => {render(<Counter />);const button = screen.getByText('Increment');fireEvent.click(button);expect(screen.getByText('Count: 1')).toBeInTheDocument();});
});
测试覆盖率
// package.json
{"scripts": {"test": "react-scripts test","test:coverage": "react-scripts test --coverage --watchAll=false"}
}
持续集成
# .github/workflows/test.yml
name: Run Tests
on: [push, pull_request]
jobs:test:runs-on: ubuntu-lateststeps:- uses: actions/checkout@v2- name: Use Node.jsuses: actions/setup-node@v2with:node-version: '14'- run: npm ci- run: npm test