React Cheat Sheet - 快速参考指南,收录常用语法、命令与实践。
React 是一个用于构建用户界面的 JavaScript 库,采用组件化开发模式。
# 创建新项目
npx create-react-app my-app
npx create-react-app my-app --template typescript
# 使用 Vite (推荐)
npm create vite@latest my-app -- --template react
npm create vite@latest my-app -- --template react-ts
JSX 是 JavaScript 的语法扩展,让你可以在 JS 中编写类 HTML 的标记。
// 基本元素
const element = <h1>Hello, World!</h1>;
// 嵌套元素 (需要单个根元素)
const component = (
<div>
<h1>标题</h1>
<p>段落</p>
</div>
);
// 使用 Fragment 避免额外 DOM 节点
const fragment = (
<>
<h1>标题</h1>
<p>段落</p>
</>
);
// 自闭合标签
const image = <img src="url" alt="描述" />;
const input = <input type="text" />;
const name = '张三';
const age = 25;
// 变量插值
const greeting = <h1>你好,{name}!</h1>;
// 表达式
const info = <p>明年 {age + 1} 岁</p>;
// 函数调用
const upper = <p>{name.toUpperCase()}</p>;
// 条件表达式
const status = <p>{age >= 18 ? '成年' : '未成年'}</p>;
// 对象属性
const user = { name: '张三', avatar: '/avatar.jpg' };
const profile = <img src={user.avatar} alt={user.name} />;
// 注释
const withComment = (
<div>
{/* 这是 JSX 注释 */}
<p>内容</p>
</div>
);
// className 代替 class
<div className="container">内容</div>
// htmlFor 代替 for
<label htmlFor="email">邮箱</label>
<input id="email" type="email" />
// style 使用对象
<div style={{ color: 'red', fontSize: '16px' }}>文本</div>
// 布尔属性
<input disabled />
<input disabled={true} />
<input disabled={isDisabled} />
// 展开属性
const props = { id: 'btn', className: 'primary', onClick: handleClick };
<button {...props}>点击</button>
// data 属性
<div data-testid="container" data-id={123}>内容</div>
// 动态属性名
const attrName = 'title';
<div {...{ [attrName]: '标题' }}>内容</div>
// 基本函数组件
function Welcome() {
return <h1>Welcome!</h1>;
}
// 箭头函数组件
const Welcome = () => {
return <h1>Welcome!</h1>;
};
// 简写 (单行返回)
const Welcome = () => <h1>Welcome!</h1>;
// 带 Props
function Greeting({ name, age = 18 }) {
return (
<div>
<h1>你好,{name}!</h1>
<p>年龄:{age}</p>
</div>
);
}
// 使用组件
function App() {
return (
<div>
<Welcome />
<Greeting name="张三" age={25} />
</div>
);
}
// 接收 Props
function UserCard({ name, email, avatar, onClick }) {
return (
<div className="user-card" onClick={onClick}>
<img src={avatar} alt={name} />
<h2>{name}</h2>
<p>{email}</p>
</div>
);
}
// 使用组件并传递 Props
<UserCard
name="张三"
email="zhangsan@example.com"
avatar="/avatar.jpg"
onClick={() => console.log('clicked')}
/>;
// 默认 Props
function Button({ text = '点击', type = 'primary' }) {
return <button className={type}>{text}</button>;
}
// children Props
function Card({ title, children }) {
return (
<div className="card">
<h2>{title}</h2>
<div className="card-body">{children}</div>
</div>
);
}
<Card title="卡片标题">
<p>这是卡片内容</p>
<button>按钮</button>
</Card>;
function Greeting({ isLoggedIn, user }) {
// if-else
if (isLoggedIn) {
return <h1>欢迎回来,{user.name}!</h1>;
}
return <h1>请登录</h1>;
}
// 三元运算符
function Message({ unread }) {
return (
<div>{unread > 0 ? <span>你有 {unread} 条未读消息</span> : <span>没有新消息</span>}</div>
);
}
// && 短路运算
function Notification({ show, message }) {
return <div>{show && <div className="notification">{message}</div>}</div>;
}
// 多条件渲染
function Status({ status }) {
const statusMap = {
pending: <span className="pending">待处理</span>,
success: <span className="success">成功</span>,
error: <span className="error">失败</span>
};
return statusMap[status] || <span>未知状态</span>;
}
function TodoList({ todos }) {
return (
<ul>
{todos.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
);
}
// 带索引的列表
function NumberList({ numbers }) {
return (
<ul>
{numbers.map((number, index) => (
<li key={index}>{number}</li>
))}
</ul>
);
}
// 对象列表
function UserList({ users }) {
return (
<div>
{users.map(user => (
<UserCard key={user.id} name={user.name} email={user.email} avatar={user.avatar} />
))}
</div>
);
}
// 过滤后渲染
function ActiveUsers({ users }) {
return (
<ul>
{users
.filter(user => user.active)
.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
import { useState } from 'react';
function Counter() {
// 基本用法
const [count, setCount] = useState(0);
return (
<div>
<p>计数: {count}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
<button onClick={() => setCount(count - 1)}>-1</button>
<button onClick={() => setCount(0)}>重置</button>
</div>
);
}
// 函数式更新 (基于前一个状态)
function Counter() {
const [count, setCount] = useState(0);
const increment = () => {
setCount(prev => prev + 1);
setCount(prev => prev + 1); // 正确累加
};
return <button onClick={increment}>{count}</button>;
}
// 惰性初始化 (复杂计算)
const [state, setState] = useState(() => {
const initialValue = expensiveComputation();
return initialValue;
});
// 对象状态
const [user, setUser] = useState({ name: '', age: 0 });
setUser(prev => ({ ...prev, name: '张三' }));
import { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
// 每次渲染后执行
useEffect(() => {
document.title = `点击了 ${count} 次`;
});
// 仅在挂载时执行
useEffect(() => {
console.log('组件已挂载');
return () => console.log('组件将卸载');
}, []);
// 依赖项变化时执行
useEffect(() => {
console.log(`count 变为 ${count}`);
}, [count]);
return <button onClick={() => setCount(count + 1)}>点击 {count} 次</button>;
}
// 数据获取
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
setLoading(true);
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => {
setUser(data);
setLoading(false);
});
}, [userId]);
if (loading) return <div>加载中...</div>;
return <div>{user.name}</div>;
}
import { useRef, useEffect } from 'react';
// DOM 引用
function TextInput() {
const inputRef = useRef(null);
const focusInput = () => {
inputRef.current.focus();
};
return (
<div>
<input ref={inputRef} type="text" />
<button onClick={focusInput}>聚焦输入框</button>
</div>
);
}
// 保存可变值 (不触发重新渲染)
function Timer() {
const [count, setCount] = useState(0);
const intervalRef = useRef(null);
useEffect(() => {
intervalRef.current = setInterval(() => {
setCount(c => c + 1);
}, 1000);
return () => clearInterval(intervalRef.current);
}, []);
const stop = () => clearInterval(intervalRef.current);
return (
<div>
<p>计数: {count}</p>
<button onClick={stop}>停止</button>
</div>
);
}
// 保存前一个值
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
}, [value]);
return ref.current;
}
import { useMemo, useCallback, useState } from 'react';
function ExpensiveComponent({ items, filter }) {
// useMemo - 缓存计算结果
const filteredItems = useMemo(() => {
console.log('过滤计算...');
return items.filter(item => item.includes(filter));
}, [items, filter]);
// useCallback - 缓存函数
const handleClick = useCallback(id => {
console.log('点击了', id);
}, []);
return (
<ul>
{filteredItems.map(item => (
<li key={item} onClick={() => handleClick(item)}>
{item}
</li>
))}
</ul>
);
}
// 避免子组件不必要的重新渲染
function Parent() {
const [count, setCount] = useState(0);
const handleSubmit = useCallback(data => {
// 提交逻辑
}, []);
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(c => c + 1)}>+1</button>
<ChildComponent onSubmit={handleSubmit} />
</div>
);
}
import { createContext, useContext, useState } from 'react';
// 创建 Context
const ThemeContext = createContext('light');
// Provider 组件
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme(prev => (prev === 'light' ? 'dark' : 'light'));
};
return <ThemeContext.Provider value={{ theme, toggleTheme }}>{children}</ThemeContext.Provider>;
}
// 消费 Context
function ThemedButton() {
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<button
onClick={toggleTheme}
style={{
background: theme === 'light' ? '#fff' : '#333',
color: theme === 'light' ? '#333' : '#fff'
}}
>
当前主题: {theme}
</button>
);
}
// 使用
function App() {
return (
<ThemeProvider>
<ThemedButton />
</ThemeProvider>
);
}
import { useReducer } from 'react';
// 定义 reducer
function todoReducer(state, action) {
switch (action.type) {
case 'ADD':
return [
...state,
{
id: Date.now(),
text: action.text,
completed: false
}
];
case 'TOGGLE':
return state.map(todo =>
todo.id === action.id ? { ...todo, completed: !todo.completed } : todo
);
case 'DELETE':
return state.filter(todo => todo.id !== action.id);
default:
return state;
}
}
function TodoApp() {
const [todos, dispatch] = useReducer(todoReducer, []);
const addTodo = text => {
dispatch({ type: 'ADD', text });
};
const toggleTodo = id => {
dispatch({ type: 'TOGGLE', id });
};
const deleteTodo = id => {
dispatch({ type: 'DELETE', id });
};
return (
<div>
<AddTodoForm onAdd={addTodo} />
<ul>
{todos.map(todo => (
<li key={todo.id}>
<span
onClick={() => toggleTodo(todo.id)}
style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}
>
{todo.text}
</span>
<button onClick={() => deleteTodo(todo.id)}>删除</button>
</li>
))}
</ul>
</div>
);
}
import { useState, useEffect } from 'react';
// 封装数据获取逻辑
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const controller = new AbortController();
setLoading(true);
fetch(url, { signal: controller.signal })
.then(res => res.json())
.then(data => {
setData(data);
setLoading(false);
})
.catch(err => {
if (err.name !== 'AbortError') {
setError(err);
setLoading(false);
}
});
return () => controller.abort();
}, [url]);
return { data, loading, error };
}
// 使用自定义 Hook
function UserList() {
const { data, loading, error } = useFetch('/api/users');
if (loading) return <div>加载中...</div>;
if (error) return <div>错误: {error.message}</div>;
return (
<ul>
{data.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
// 本地存储 Hook
function useLocalStorage(key, initialValue) {
const [value, setValue] = useState(() => {
const stored = localStorage.getItem(key);
return stored ? JSON.parse(stored) : initialValue;
});
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value));
}, [key, value]);
return [value, setValue];
}
import { useId, useTransition, useDeferredValue } from 'react';
// useId - 生成唯一 ID
function FormField({ label }) {
const id = useId();
return (
<div>
<label htmlFor={id}>{label}</label>
<input id={id} type="text" />
</div>
);
}
// useTransition - 标记非紧急更新
function SearchResults({ query }) {
const [isPending, startTransition] = useTransition();
const [results, setResults] = useState([]);
const handleSearch = value => {
startTransition(() => {
// 非紧急更新
setResults(filterResults(value));
});
};
return (
<div>
<input onChange={e => handleSearch(e.target.value)} />
{isPending ? <div>搜索中...</div> : <ResultList items={results} />}
</div>
);
}
// useDeferredValue - 延迟更新值
function SearchPage() {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
return (
<div>
<input value={query} onChange={e => setQuery(e.target.value)} />
<SearchResults query={deferredQuery} />
</div>
);
}
import { useState } from 'react';
function ContactForm() {
const [formData, setFormData] = useState({
name: '',
email: '',
message: ''
});
const handleChange = e => {
const { name, value } = e.target;
setFormData(prev => ({
...prev,
[name]: value
}));
};
const handleSubmit = e => {
e.preventDefault();
console.log('提交数据:', formData);
};
return (
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="name">姓名:</label>
<input id="name" name="name" value={formData.name} onChange={handleChange} />
</div>
<div>
<label htmlFor="email">邮箱:</label>
<input
id="email"
name="email"
type="email"
value={formData.email}
onChange={handleChange}
/>
</div>
<div>
<label htmlFor="message">留言:</label>
<textarea
id="message"
name="message"
value={formData.message}
onChange={handleChange}
/>
</div>
<button type="submit">提交</button>
</form>
);
}
import { useState } from 'react';
function LoginForm() {
const [values, setValues] = useState({ email: '', password: '' });
const [errors, setErrors] = useState({});
const [touched, setTouched] = useState({});
const validate = (name, value) => {
switch (name) {
case 'email':
if (!value) return '邮箱不能为空';
if (!/\S+@\S+\.\S+/.test(value)) return '邮箱格式不正确';
return '';
case 'password':
if (!value) return '密码不能为空';
if (value.length < 6) return '密码至少6位';
return '';
default:
return '';
}
};
const handleChange = e => {
const { name, value } = e.target;
setValues(prev => ({ ...prev, [name]: value }));
setErrors(prev => ({ ...prev, [name]: validate(name, value) }));
};
const handleBlur = e => {
const { name } = e.target;
setTouched(prev => ({ ...prev, [name]: true }));
};
const handleSubmit = e => {
e.preventDefault();
const newErrors = {
email: validate('email', values.email),
password: validate('password', values.password)
};
setErrors(newErrors);
setTouched({ email: true, password: true });
if (!newErrors.email && !newErrors.password) {
console.log('提交:', values);
}
};
return (
<form onSubmit={handleSubmit}>
<div>
<input
name="email"
value={values.email}
onChange={handleChange}
onBlur={handleBlur}
placeholder="邮箱"
/>
{touched.email && errors.email && <span className="error">{errors.email}</span>}
</div>
<div>
<input
name="password"
type="password"
value={values.password}
onChange={handleChange}
onBlur={handleBlur}
placeholder="密码"
/>
{touched.password && errors.password && (
<span className="error">{errors.password}</span>
)}
</div>
<button type="submit">登录</button>
</form>
);
}
import { useState } from 'react';
function useForm(initialValues, validate) {
const [values, setValues] = useState(initialValues);
const [errors, setErrors] = useState({});
const [isSubmitting, setIsSubmitting] = useState(false);
const handleChange = e => {
const { name, value, type, checked } = e.target;
setValues(prev => ({
...prev,
[name]: type === 'checkbox' ? checked : value
}));
};
const handleSubmit = async callback => {
return async e => {
e.preventDefault();
const validationErrors = validate ? validate(values) : {};
setErrors(validationErrors);
if (Object.keys(validationErrors).length === 0) {
setIsSubmitting(true);
try {
await callback(values);
} finally {
setIsSubmitting(false);
}
}
};
};
const reset = () => {
setValues(initialValues);
setErrors({});
};
return {
values,
errors,
isSubmitting,
handleChange,
handleSubmit,
reset,
setValues
};
}
// 使用
function SignupForm() {
const { values, errors, handleChange, handleSubmit, isSubmitting } = useForm(
{ name: '', email: '', password: '' },
values => {
const errors = {};
if (!values.name) errors.name = '姓名必填';
if (!values.email) errors.email = '邮箱必填';
if (!values.password) errors.password = '密码必填';
return errors;
}
);
const onSubmit = async data => {
await api.signup(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
{/* 表单字段 */}
<button disabled={isSubmitting}>{isSubmitting ? '提交中...' : '注册'}</button>
</form>
);
}
// CSS 类名
import './Button.css';
function Button({ variant = 'primary', children }) {
return <button className={`btn btn-${variant}`}>{children}</button>;
}
// 条件类名
function NavLink({ isActive, children }) {
return <a className={`nav-link ${isActive ? 'active' : ''}`}>{children}</a>;
}
// 多个条件类名
function Card({ featured, disabled }) {
const classNames = ['card', featured && 'card-featured', disabled && 'card-disabled']
.filter(Boolean)
.join(' ');
return <div className={classNames}>...</div>;
}
// 内联样式
function Box({ width, height, color }) {
const style = {
width: `${width}px`,
height: `${height}px`,
backgroundColor: color,
borderRadius: '8px'
};
return <div style={style}>内容</div>;
}
// Button.module.css
/*
.button {
padding: 10px 20px;
border-radius: 4px;
}
.primary {
background: blue;
color: white;
}
.secondary {
background: gray;
color: white;
}
*/
import styles from './Button.module.css';
function Button({ variant = 'primary', children }) {
return <button className={`${styles.button} ${styles[variant]}`}>{children}</button>;
}
// 动态类名
function Card({ isActive }) {
return <div className={isActive ? styles.active : styles.inactive}>内容</div>;
}
import styled from 'styled-components';
// 基本样式组件
const Button = styled.button`
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
background: ${props => (props.primary ? 'blue' : 'gray')};
color: white;
&:hover {
opacity: 0.8;
}
`;
// 继承样式
const PrimaryButton = styled(Button)`
background: #007bff;
`;
// 根据 props 动态样式
const Input = styled.input`
padding: 8px 12px;
border: 2px solid ${props => (props.error ? 'red' : '#ddd')};
border-radius: 4px;
&:focus {
border-color: ${props => (props.error ? 'red' : 'blue')};
outline: none;
}
`;
// 使用
function Form() {
return (
<div>
<Input placeholder="姓名" />
<Input placeholder="邮箱" error />
<Button>普通按钮</Button>
<Button primary>主要按钮</Button>
</div>
);
}
import { memo, useState } from 'react';
// 使用 memo 包裹组件
const ExpensiveComponent = memo(function ExpensiveComponent({ data }) {
console.log('ExpensiveComponent 渲染');
return (
<div>
{data.map(item => (
<div key={item.id}>{item.name}</div>
))}
</div>
);
});
// 自定义比较函数
const MemoizedComponent = memo(
function MyComponent({ user, onClick }) {
return <div onClick={onClick}>{user.name}</div>;
},
(prevProps, nextProps) => {
// 返回 true 表示不重新渲染
return prevProps.user.id === nextProps.user.id;
}
);
// 父组件
function Parent() {
const [count, setCount] = useState(0);
const [data] = useState([
{ id: 1, name: 'Item 1' },
{ id: 2, name: 'Item 2' }
]);
return (
<div>
<button onClick={() => setCount(c => c + 1)}>计数: {count}</button>
{/* data 不变时,ExpensiveComponent 不会重新渲染 */}
<ExpensiveComponent data={data} />
</div>
);
}
import { lazy, Suspense } from 'react';
// 懒加载组件
const HeavyComponent = lazy(() => import('./HeavyComponent'));
const Dashboard = lazy(() => import('./Dashboard'));
function App() {
return (
<div>
<Suspense fallback={<div>加载中...</div>}>
<HeavyComponent />
</Suspense>
</div>
);
}
// 路由级别的代码分割
import { BrowserRouter, Routes, Route } from 'react-router-dom';
const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
const Contact = lazy(() => import('./pages/Contact'));
function App() {
return (
<BrowserRouter>
<Suspense fallback={<div>页面加载中...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/contact" element={<Contact />} />
</Routes>
</Suspense>
</BrowserRouter>
);
}
// 带错误边界
import { ErrorBoundary } from 'react-error-boundary';
<ErrorBoundary fallback={<div>加载失败</div>}>
<Suspense fallback={<div>加载中...</div>}>
<HeavyComponent />
</Suspense>
</ErrorBoundary>;
import { useRef, useState, useCallback } from 'react';
// 简单的虚拟列表实现
function VirtualList({ items, itemHeight, containerHeight }) {
const [scrollTop, setScrollTop] = useState(0);
const containerRef = useRef(null);
const startIndex = Math.floor(scrollTop / itemHeight);
const endIndex = Math.min(
startIndex + Math.ceil(containerHeight / itemHeight) + 1,
items.length
);
const visibleItems = items.slice(startIndex, endIndex);
const totalHeight = items.length * itemHeight;
const offsetY = startIndex * itemHeight;
const handleScroll = useCallback(e => {
setScrollTop(e.target.scrollTop);
}, []);
return (
<div
ref={containerRef}
style={{ height: containerHeight, overflow: 'auto' }}
onScroll={handleScroll}
>
<div style={{ height: totalHeight, position: 'relative' }}>
<div style={{ transform: `translateY(${offsetY}px)` }}>
{visibleItems.map((item, index) => (
<div key={startIndex + index} style={{ height: itemHeight }}>
{item.content}
</div>
))}
</div>
</div>
</div>
);
}
// 推荐使用专业库
// npm install react-window
import { FixedSizeList } from 'react-window';
function List({ items }) {
const Row = ({ index, style }) => <div style={style}>{items[index].name}</div>;
return (
<FixedSizeList height={400} itemCount={items.length} itemSize={35} width="100%">
{Row}
</FixedSizeList>
);
}
// 复合组件模式
function Tabs({ children, defaultValue }) {
const [activeTab, setActiveTab] = useState(defaultValue);
return (
<TabsContext.Provider value={{ activeTab, setActiveTab }}>
<div className="tabs">{children}</div>
</TabsContext.Provider>
);
}
Tabs.List = function TabsList({ children }) {
return <div className="tabs-list">{children}</div>;
};
Tabs.Tab = function Tab({ value, children }) {
const { activeTab, setActiveTab } = useContext(TabsContext);
return (
<button className={activeTab === value ? 'active' : ''} onClick={() => setActiveTab(value)}>
{children}
</button>
);
};
Tabs.Panel = function TabsPanel({ value, children }) {
const { activeTab } = useContext(TabsContext);
if (activeTab !== value) return null;
return <div className="tabs-panel">{children}</div>;
};
// 使用
<Tabs defaultValue="tab1">
<Tabs.List>
<Tabs.Tab value="tab1">标签1</Tabs.Tab>
<Tabs.Tab value="tab2">标签2</Tabs.Tab>
</Tabs.List>
<Tabs.Panel value="tab1">内容1</Tabs.Panel>
<Tabs.Panel value="tab2">内容2</Tabs.Panel>
</Tabs>;
// Render Props 模式
function MouseTracker({ render }) {
const [position, setPosition] = useState({ x: 0, y: 0 });
const handleMouseMove = e => {
setPosition({ x: e.clientX, y: e.clientY });
};
return (
<div onMouseMove={handleMouseMove} style={{ height: '100vh' }}>
{render(position)}
</div>
);
}
// 使用
<MouseTracker
render={({ x, y }) => (
<p>
鼠标位置: ({x}, {y})
</p>
)}
/>;
// children 作为函数
function DataFetcher({ url, children }) {
const { data, loading, error } = useFetch(url);
return children({ data, loading, error });
}
<DataFetcher url="/api/users">
{({ data, loading, error }) => {
if (loading) return <div>加载中...</div>;
if (error) return <div>错误: {error.message}</div>;
return <UserList users={data} />;
}}
</DataFetcher>;
// 高阶组件
function withLoading(WrappedComponent) {
return function WithLoading({ isLoading, ...props }) {
if (isLoading) {
return <div>加载中...</div>;
}
return <WrappedComponent {...props} />;
};
}
// 使用
const UserListWithLoading = withLoading(UserList);
<UserListWithLoading isLoading={loading} users={users} />;
// 带配置的 HOC
function withAuth(WrappedComponent, { redirectTo = '/login' } = {}) {
return function WithAuth(props) {
const { user } = useAuth();
if (!user) {
return <Navigate to={redirectTo} />;
}
return <WrappedComponent {...props} user={user} />;
};
}
const ProtectedDashboard = withAuth(Dashboard);
const ProtectedSettings = withAuth(Settings, { redirectTo: '/signin' });
import { Component } from 'react';
class ErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
componentDidCatch(error, errorInfo) {
console.error('错误边界捕获:', error, errorInfo);
// 发送错误到日志服务
}
render() {
if (this.state.hasError) {
return (
this.props.fallback || (
<div className="error-fallback">
<h2>出错了</h2>
<p>{this.state.error?.message}</p>
<button onClick={() => this.setState({ hasError: false })}>重试</button>
</div>
)
);
}
return this.props.children;
}
}
// 使用
<ErrorBoundary fallback={<div>加载失败</div>}>
<MyComponent />
</ErrorBoundary>;
// 推荐使用 react-error-boundary 库
import { ErrorBoundary } from 'react-error-boundary';
function ErrorFallback({ error, resetErrorBoundary }) {
return (
<div>
<p>出错了: {error.message}</p>
<button onClick={resetErrorBoundary}>重试</button>
</div>
);
}
<ErrorBoundary
FallbackComponent={ErrorFallback}
onReset={() => {
// 重置应用状态
}}
>
<MyComponent />
</ErrorBoundary>;
地址
Level 10b, 144 Edward Street, Brisbane CBD(Headquarter)Level 2, 171 La Trobe St, Melbourne VIC 3000四川省成都市武侯区桂溪街道天府大道中段500号D5东方希望天祥广场B座45A13号Business Hub, 155 Waymouth St, Adelaide SA 5000Disclaimer
JR Academy acknowledges Traditional Owners of Country throughout Australia and recognises the continuing connection to lands, waters and communities. We pay our respect to Aboriginal and Torres Strait Islander cultures; and to Elders past and present. Aboriginal and Torres Strait Islander peoples should be aware that this website may contain images or names of people who have since passed away.
匠人学院网站上的所有内容,包括课程材料、徽标和匠人学院网站上提供的信息,均受澳大利亚政府知识产权法的保护。严禁未经授权使用、销售、分发、复制或修改。违规行为可能会导致法律诉讼。通过访问我们的网站,您同意尊重我们的知识产权。 JR Academy Pty Ltd 保留所有权利,包括专利、商标和版权。任何侵权行为都将受到法律追究。查看用户协议
© 2017-2025 JR Academy Pty Ltd. All rights reserved.
ABN 26621887572