组件结构
// 使用函数组件和 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-latest
steps:
- uses: actions/checkout@v2
- name: Use Node.js
uses: actions/setup-node@v2
with:
node-version: '14'
- run: npm ci
- run: npm test