React JSX 语法详解

第二章 JSX 语法详解

JSX(JavaScript XML)是 React 的核心语法扩展,它允许我们在 JavaScript 中编写类似 HTML 的代码,使得组件的结构更加直观和易于理解。

2.1 JSX 基础概念

2.1.1 JSX 的本质

JSX 编译过程:

graph TD A[JSX 代码] --> B[Babel 编译器] B --> C[React.createElement 调用] C --> D[Virtual DOM 对象] D --> E[真实 DOM] F[TypeScript] --> G[类型检查] G --> A

JSX 转换示例:

// JSX 代码
const element = (
  <div className="container">
    <h1>Hello World</h1>
    <p>Current time: {new Date().toLocaleTimeString()}</p>
  </div>
);

// 编译后的 JavaScript 代码(React 17+)
const element = React.createElement(
  'div',
  { className: 'container' },
  React.createElement('h1', null, 'Hello World'),
  React.createElement(
    'p', 
    null, 
    'Current time: ', 
    new Date().toLocaleTimeString()
  )
);

// React 17+ 自动转换(不需要手动导入 React)
import { jsx as _jsx } from 'react/jsx-runtime';
const element = _jsx('div', {
  className: 'container',
  children: [
    _jsx('h1', { children: 'Hello World' }),
    _jsx('p', { 
      children: ['Current time: ', new Date().toLocaleTimeString()] 
    })
  ]
});

2.1.2 JSX 与 HTML 的区别

语法差异对比表:

特性 HTML JSX 说明
属性命名 class className 避免与 JavaScript 关键字冲突
标签闭合 <img>, <br> <img />, <br /> 所有标签必须闭合
布尔属性 disabled disabled={true} 布尔值需显式指定
样式属性 style="color: red" style={{ color: 'red' }} 使用对象语法
注释 <!-- comment --> {/* comment */} JavaScript 注释语法
自定义属性 data-* data-* data 属性保持一致
事件命名 onclick onClick 驼峰命名法

具体示例对比:

<!-- HTML 代码 -->
<div class="container" style="margin: 0 auto; width: 100%">
  <img src="logo.png" alt="Logo" class="logo" />
  <button onclick="handleClick()" disabled>Submit</button>
  <input type="text" placeholder="Enter name" required />
  <!-- This is a comment -->
</div>
// JSX 代码
<div className="container" style={{ margin: '0 auto', width: '100%' }}>
  <img src="logo.png" alt="Logo" className="logo" />
  <button onClick={handleClick} disabled={true}>Submit</button>
  <input type="text" placeholder="Enter name" required={true} />
  {/* This is a comment */}
</div>

2.1.3 JSX 的类型定义

TypeScript JSX 类型:

// JSX.Element 类型
const element: JSX.Element = <div>Hello</div>;

// JSX.IntrinsicElements - 内置元素类型
declare global {
  namespace JSX {
    interface IntrinsicElements {
      div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
      span: React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>;
      button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>;
      // ... 其他内置元素
    }
  }
}

// 自定义组件类型
interface CustomComponentProps {
  title: string;
  count: number;
  onAction?: () => void;
}

const CustomComponent: React.FC<CustomComponentProps> = ({ title, count, onAction }) => {
  return (
    <div>
      <h2>{title}</h2>
      <p>Count: {count}</p>
      {onAction && <button onClick={onAction}>Action</button>}
    </div>
  );
};

// 使用自定义组件
const App = () => {
  return (
    <CustomComponent 
      title="My App" 
      count={42} 
      onAction={() => console.log('Action triggered')}
    />
  );
};

2.2 JSX 核心语法

2.2.1 元素与组件

函数组件定义:

// 基础函数组件
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

// 箭头函数组件
const Welcome = (props) => {
  return <h1>Hello, {props.name}</h1>;
};

// 箭头函数组件(隐式返回)
const Welcome = (props) => <h1>Hello, {props.name}</h1>;

// TypeScript 组件
interface WelcomeProps {
  name: string;
  age?: number;
}

const Welcome: React.FC<WelcomeProps> = ({ name, age }) => (
  <h1>Hello, {name}!{age && ` Age: ${age}`}</h1>
);

// 使用组件
function App() {
  return (
    <div>
      <Welcome name="Alice" />
      <Welcome name="Bob" age={25} />
    </div>
  );
}

类组件定义:

import React, { Component } from 'react';

class Welcome extends Component {
  constructor(props) {
    super(props);
    this.state = {
      message: 'Welcome to React!'
    };
  }

  render() {
    const { name } = this.props;
    const { message } = this.state;

    return (
      <div>
        <h1>Hello, {name}</h1>
        <p>{message}</p>
      </div>
    );
  }
}

// TypeScript 类组件
interface WelcomeProps {
  name: string;
}

interface WelcomeState {
  message: string;
}

class Welcome extends Component<WelcomeProps, WelcomeState> {
  constructor(props: WelcomeProps) {
    super(props);
    this.state = {
      message: 'Welcome to React!'
    };
  }

  render(): React.ReactNode {
    const { name } = this.props;
    const { message } = this.state;

    return (
      <div>
        <h1>Hello, {name}</h1>
        <p>{message}</p>
      </div>
    );
  }
}

2.2.2 Props 与属性

Props 传递方式:

// 基础 Props 传递
const UserProfile = (props) => {
  return (
    <div>
      <h2>{props.name}</h2>
      <p>Age: {props.age}</p>
      <p>Email: {props.email}</p>
    </div>
  );
};

// 解构 Props
const UserProfile = ({ name, age, email }) => {
  return (
    <div>
      <h2>{name}</h2>
      <p>Age: {age}</p>
      <p>Email: {email}</p>
    </div>
  );
};

// 使用组件
function App() {
  const user = {
    name: 'John Doe',
    age: 30,
    email: 'john@example.com'
  };

  return (
    <div>
      {/* 分别传递属性 */}
      <UserProfile 
        name="Alice" 
        age={25} 
        email="alice@example.com" 
      />
      
      {/* 使用展开运算符 */}
      <UserProfile {...user} />
      
      {/* 混合使用 */}
      <UserProfile 
        {...user} 
        name="Bob" // 覆盖 name 属性
      />
    </div>
  );
}

Props 验证与默认值:

import PropTypes from 'prop-types';

const Button = ({ 
  text, 
  onClick, 
  variant = 'primary', 
  disabled = false,
  size = 'medium'
}) => {
  const baseClasses = 'btn';
  const variantClasses = `btn-${variant}`;
  const sizeClasses = `btn-${size}`;
  const disabledClasses = disabled ? 'btn-disabled' : '';

  return (
    <button
      className={`${baseClasses} ${variantClasses} ${sizeClasses} ${disabledClasses}`}
      onClick={onClick}
      disabled={disabled}
    >
      {text}
    </button>
  );
};

// Props 类型验证
Button.propTypes = {
  text: PropTypes.string.isRequired,
  onClick: PropTypes.func,
  variant: PropTypes.oneOf(['primary', 'secondary', 'danger', 'success']),
  disabled: PropTypes.bool,
  size: PropTypes.oneOf(['small', 'medium', 'large']),
};

// 默认 Props
Button.defaultProps = {
  variant: 'primary',
  disabled: false,
  size: 'medium',
  onClick: () => {},
};

// TypeScript 类型定义
interface ButtonProps {
  text: string;
  onClick?: () => void;
  variant?: 'primary' | 'secondary' | 'danger' | 'success';
  disabled?: boolean;
  size?: 'small' | 'medium' | 'large';
}

const Button: React.FC<ButtonProps> = ({
  text,
  onClick,
  variant = 'primary',
  disabled = false,
  size = 'medium'
}) => {
  // 组件实现
};

2.2.3 Children 与内容

Children 传递方式:

// 基础 Children 使用
const Card = ({ children, title }) => {
  return (
    <div className="card">
      {title && <div className="card-header">{title}</div>}
      <div className="card-body">
        {children}
      </div>
    </div>
  );
};

// 使用 Card 组件
function App() {
  return (
    <div>
      {/* 传递文本内容 */}
      <Card title="Simple Card">
        This is a simple card content.
      </Card>

      {/* 传递多个元素 */}
      <Card title="Complex Card">
        <h3>Subtitle</h3>
        <p>Paragraph content.</p>
        <button>Action Button</button>
      </Card>

      {/* 空内容 */}
      <Card title="Empty Card">
        {/* 可以显示空状态 */}
        <p>No content available.</p>
      </Card>
    </div>
  );
}

Children 操作函数:

import React, { Children, cloneElement, isValidElement } from 'react';

const TabContainer = ({ children, activeIndex, onTabChange }) => {
  const tabs = Children.toArray(children);

  return (
    <div className="tab-container">
      <div className="tab-nav">
        {tabs.map((child, index) => {
          if (!isValidElement(child)) return null;
          
          const isActive = index === activeIndex;
          const { title } = child.props;

          return (
            <button
              key={index}
              className={`tab-button ${isActive ? 'active' : ''}`}
              onClick={() => onTabChange(index)}
            >
              {title}
            </button>
          );
        })}
      </div>

      <div className="tab-content">
        {tabs.map((child, index) => {
          if (!isValidElement(child)) return null;

          return (
            <div
              key={index}
              className={`tab-panel ${index === activeIndex ? 'active' : ''}`}
            >
              {index === activeIndex ? child : null}
            </div>
          );
        })}
      </div>
    </div>
  );
};

const TabPanel = ({ title, children }) => {
  return <div>{children}</div>;
};

// 使用示例
function App() {
  const [activeTab, setActiveTab] = React.useState(0);

  return (
    <TabContainer 
      activeIndex={activeTab} 
      onTabChange={setActiveTab}
    >
      <TabPanel title="Profile">
        <h3>User Profile</h3>
        <p>Profile information goes here.</p>
      </TabPanel>
      <TabPanel title="Settings">
        <h3>Settings</h3>
        <p>Settings configuration goes here.</p>
      </TabPanel>
      <TabPanel title="About">
        <h3>About</h3>
        <p>About information goes here.</p>
      </TabPanel>
    </TabContainer>
  );
}

Children 类型检查:

import { Children, isValidElement } from 'react';

const Menu = ({ children }) => {
  // 检查 children 是否为 MenuItem
  const menuItems = Children.toArray(children).filter(child => 
    isValidElement(child) && child.type === MenuItem
  );

  if (menuItems.length === 0) {
    console.warn('Menu should contain at least one MenuItem');
    return null;
  }

  return (
    <ul className="menu">
      {menuItems.map((child, index) => (
        <li key={index} className="menu-item">
          {child}
        </li>
      ))}
    </ul>
  );
};

const MenuItem = ({ children, onClick, disabled = false }) => {
  return (
    <button 
      className={`menu-button ${disabled ? 'disabled' : ''}`}
      onClick={onClick}
      disabled={disabled}
    >
      {children}
    </button>
  );
};

// 使用示例
function App() {
  return (
    <Menu>
      <MenuItem onClick={() => console.log('Home clicked')}>
        Home
      </MenuItem>
      <MenuItem onClick={() => console.log('About clicked')}>
        About
      </MenuItem>
      <MenuItem onClick={() => console.log('Contact clicked')}>
        Contact
      </MenuItem>
    </Menu>
  );
}

2.3 JSX 表达式与插值

2.3.1 基础表达式插值

JavaScript 表达式嵌入:

import React, { useState } from 'react';

const ExpressionDemo = () => {
  const [count, setCount] = useState(0);
  const [name, setName] = useState('');
  const [isLogin, setIsLogin] = useState(false);
  const items = ['Apple', 'Banana', 'Orange'];
  const user = {
    id: 1,
    name: 'John Doe',
    email: 'john@example.com'
  };

  // 计算属性
  const greeting = `Hello, ${user.name}!`;
  const itemCount = items.length;
  const isEven = count % 2 === 0;

  return (
    <div className="expression-demo">
      {/* 变量插值 */}
      <h1>{greeting}</h1>
      
      {/* 算术运算 */}
      <p>Count: {count}</p>
      <p>Double: {count * 2}</p>
      <p>Is even: {isEven ? 'Yes' : 'No'}</p>
      
      {/* 字符串操作 */}
      <input
        type="text"
        value={name}
        onChange={(e) => setName(e.target.value)}
        placeholder="Enter your name"
      />
      {name && <p>Hello, {name.toUpperCase()}!</p>}
      
      {/* 条件渲染 */}
      <div>
        {isLogin ? (
          <p>Welcome back!</p>
        ) : (
          <p>Please log in</p>
        )}
      </div>
      
      {/* 数组操作 */}
      <ul>
        {items.map((item, index) => (
          <li key={index}>
            {index + 1}. {item}
          </li>
        ))}
      </ul>
      
      {/* 对象操作 */}
      <div className="user-card">
        <h2>{user.name}</h2>
        <p>Email: {user.email}</p>
        <p>ID: {String(user.id).padStart(6, '0')}</p>
      </div>
      
      {/* 日期格式化 */}
      <p>Current time: {new Date().toLocaleTimeString()}</p>
      <p>Formatted date: {new Date().toLocaleDateString('zh-CN')}</p>
      
      {/* 函数调用 */}
      <button onClick={() => setCount(count + 1)}>
        Increment: {count + 1}
      </button>
    </div>
  );
};

2.3.2 条件渲染

多种条件渲染方式:

import React, { useState } from 'react';

const ConditionalRendering = () => {
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState(null);
  const [data, setData] = useState(null);
  const [userType, setUserType] = useState('guest');

  // 模拟数据获取
  const fetchData = async () => {
    setIsLoading(true);
    setError(null);
    
    try {
      // 模拟 API 调用
      await new Promise(resolve => setTimeout(resolve, 1000));
      setData({ message: 'Data loaded successfully!' });
    } catch (err) {
      setError('Failed to load data');
    } finally {
      setIsLoading(false);
    }
  };

  // 1. 三元运算符条件渲染
  const renderStatus = () => {
    return isLoading ? (
      <div className="loading">Loading...</div>
    ) : error ? (
      <div className="error">Error: {error}</div>
    ) : data ? (
      <div className="success">{data.message}</div>
    ) : (
      <div className="idle">No data loaded</div>
    );
  };

  return (
    <div className="conditional-rendering">
      <h2>Conditional Rendering Examples</h2>

      {/* 三元运算符 */}
      <div className="example">
        <h3>1. Ternary Operator</h3>
        <div className="status">
          {isLoading ? 'Loading...' : 'Load Complete'}
        </div>
      </div>

      {/* 逻辑与 (&&) */}
      <div className="example">
        <h3>2. Logical AND</h3>
        {error && <div className="error">Error: {error}</div>}
        {data && <div className="data">{data.message}</div>}
        {!isLoading && !error && !data && (
          <div className="no-data">No data available</div>
        )}
      </div>

      {/* 函数条件渲染 */}
      <div className="example">
        <h3>3. Function Rendering</h3>
        {renderStatus()}
      </div>

      {/* 多条件渲染 */}
      <div className="example">
        <h3>4. Multiple Conditions</h3>
        <div className="user-info">
          {(() => {
            switch (userType) {
              case 'admin':
                return <span className="admin">Admin User</span>;
              case 'user':
                return <span className="user">Regular User</span>;
              case 'guest':
                return <span className="guest">Guest User</span>;
              default:
                return <span className="unknown">Unknown User</span>;
            }
          })()}
        </div>
        
        <div>
          <label>User Type:</label>
          <select 
            value={userType} 
            onChange={(e) => setUserType(e.target.value)}
          >
            <option value="guest">Guest</option>
            <option value="user">User</option>
            <option value="admin">Admin</option>
          </select>
        </div>
      </div>

      {/* 复杂条件渲染 */}
      <div className="example">
        <h3>5. Complex Conditions</h3>
        <div className="actions">
          {data && !isLoading && (
            <div className="action-buttons">
              {data.message.includes('success') && (
                <button className="success-btn">Success Action</button>
              )}
              {data.message.includes('error') && (
                <button className="error-btn">Error Action</button>
              )}
              {data.message.length > 20 && (
                <button className="long-btn">Long Message Action</button>
              )}
            </div>
          )}
        </div>
      </div>

      {/* 控制按钮 */}
      <div className="controls">
        <button onClick={fetchData} disabled={isLoading}>
          {isLoading ? 'Loading...' : 'Load Data'}
        </button>
        <button onClick={() => setError(null)}>Clear Error</button>
        <button onClick={() => setData(null)}>Clear Data</button>
      </div>
    </div>
  );
};

2.3.3 列表渲染

基础列表渲染:

import React, { useState } from 'react';

const ListRendering = () => {
  const [items, setItems] = useState([
    { id: 1, name: 'Apple', price: 2.5, category: 'fruit' },
    { id: 2, name: 'Banana', price: 1.2, category: 'fruit' },
    { id: 3, name: 'Carrot', price: 0.8, category: 'vegetable' },
    { id: 4, name: 'Bread', price: 3.0, category: 'grain' },
  ]);

  const [filter, setFilter] = useState('all');

  // 过滤和排序逻辑
  const filteredItems = items
    .filter(item => filter === 'all' || item.category === filter)
    .sort((a, b) => a.name.localeCompare(b.name));

  return (
    <div className="list-rendering">
      <h2>List Rendering Examples</h2>

      {/* 基础列表 */}
      <div className="example">
        <h3>1. Basic List</h3>
        <ul>
          {items.map(item => (
            <li key={item.id}>
              {item.name} - ${item.price}
            </li>
          ))}
        </ul>
      </div>

      {/* 带组件的列表 */}
      <div className="example">
        <h3>2. List with Components</h3>
        <div className="item-grid">
          {filteredItems.map(item => (
            <ItemCard 
              key={item.id} 
              item={item}
              onEdit={() => console.log('Edit:', item.id)}
              onDelete={() => {
                setItems(items.filter(i => i.id !== item.id));
              }}
            />
          ))}
        </div>
      </div>

      {/* 分组列表 */}
      <div className="example">
        <h3>3. Grouped List</h3>
        <div className="grouped-items">
          {['fruit', 'vegetable', 'grain'].map(category => {
            const categoryItems = items.filter(item => item.category === category);
            if (categoryItems.length === 0) return null;

            return (
              <div key={category} className="category-group">
                <h4>{category.charAt(0).toUpperCase() + category.slice(1)}</h4>
                <ul>
                  {categoryItems.map(item => (
                    <li key={item.id}>{item.name}</li>
                  ))}
                </ul>
              </div>
            );
          })}
        </div>
      </div>

      {/* 动态列表操作 */}
      <div className="example">
        <h3>4. Dynamic List Operations</h3>
        <div className="filters">
          <button 
            onClick={() => setFilter('all')}
            className={filter === 'all' ? 'active' : ''}
          >
            All
          </button>
          <button 
            onClick={() => setFilter('fruit')}
            className={filter === 'fruit' ? 'active' : ''}
          >
            Fruits
          </button>
          <button 
            onClick={() => setFilter('vegetable')}
            className={filter === 'vegetable' ? 'active' : ''}
          >
            Vegetables
          </button>
          <button 
            onClick={() => setFilter('grain')}
            className={filter === 'grain' ? 'active' : ''}
          >
            Grains
          </button>
        </div>

        <button 
          onClick={() => {
            const newItem = {
              id: Date.now(),
              name: `New Item ${items.length + 1}`,
              price: Math.random() * 5 + 0.5,
              category: 'fruit'
            };
            setItems([...items, newItem]);
          }}
        >
          Add Item
        </button>
      </div>

      {/* 嵌套列表 */}
      <div className="example">
        <h3>5. Nested List</h3>
        <div className="nested-list">
          {[
            {
              title: 'Fruits',
              items: ['Apple', 'Banana', 'Orange']
            },
            {
              title: 'Vegetables', 
              items: ['Carrot', 'Broccoli', 'Spinach']
            },
            {
              title: 'Grains',
              items: ['Wheat', 'Rice', 'Corn']
            }
          ].map(category => (
            <div key={category.title} className="category">
              <h4>{category.title}</h4>
              <ul>
                {category.items.map((item, index) => (
                  <li key={index}>{item}</li>
                ))}
              </ul>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
};

// 列表项组件
const ItemCard = ({ item, onEdit, onDelete }) => {
  const [isHovered, setIsHovered] = useState(false);

  return (
    <div 
      className="item-card"
      onMouseEnter={() => setIsHovered(true)}
      onMouseLeave={() => setIsHovered(false)}
    >
      <h4>{item.name}</h4>
      <p>Price: ${item.price.toFixed(2)}</p>
      <p>Category: {item.category}</p>
      
      {isHovered && (
        <div className="actions">
          <button onClick={onEdit}>Edit</button>
          <button onClick={onDelete}>Delete</button>
        </div>
      )}
    </div>
  );
};

2.4 JSX 事件处理

2.4.1 事件绑定语法

基础事件处理:

import React, { useState } from 'react';

const EventHandling = () => {
  const [count, setCount] = useState(0);
  const [message, setMessage] = useState('');
  const [isToggle, setIsToggle] = useState(false);

  // 基础事件处理函数
  const handleClick = () => {
    setCount(count + 1);
    console.log('Button clicked!');
  };

  // 带参数的事件处理
  const handleIncrement = (amount) => {
    setCount(count + amount);
  };

  // 使用箭头函数传递参数
  const handleButtonClick = (value) => {
    setMessage(`Button ${value} clicked!`);
  };

  // 表单事件处理
  const handleInputChange = (e) => {
    setMessage(e.target.value);
  };

  const handleSubmit = (e) => {
    e.preventDefault(); // 阻止默认行为
    alert(`Form submitted with: ${message}`);
  };

  // 键盘事件处理
  const handleKeyPress = (e) => {
    if (e.key === 'Enter') {
      setMessage(`Enter pressed! Current count: ${count}`);
    }
  };

  // 鼠标事件处理
  const handleMouseEnter = () => {
    setIsToggle(true);
  };

  const handleMouseLeave = () => {
    setIsToggle(false);
  };

  return (
    <div className="event-handling">
      <h2>Event Handling Examples</h2>

      {/* 基础点击事件 */}
      <div className="example">
        <h3>1. Basic Click Event</h3>
        <button onClick={handleClick}>
          Click Count: {count}
        </button>
        <button onClick={() => handleIncrement(5)}>
          Add 5
        </button>
        <button onClick={() => handleIncrement(-1)}>
          Subtract 1
        </button>
      </div>

      {/* 表单事件 */}
      <div className="example">
        <h3>2. Form Events</h3>
        <form onSubmit={handleSubmit}>
          <input
            type="text"
            value={message}
            onChange={handleInputChange}
            onKeyPress={handleKeyPress}
            placeholder="Type something..."
          />
          <button type="submit">Submit</button>
        </form>
        {message && <p>Message: {message}</p>}
      </div>

      {/* 按钮组事件 */}
      <div className="example">
        <h3>3. Button Group Events</h3>
        {['A', 'B', 'C'].map((value) => (
          <button
            key={value}
            onClick={() => handleButtonClick(value)}
            style={{ margin: '5px' }}
          >
            Button {value}
          </button>
        ))}
      </div>

      {/* 鼠标事件 */}
      <div className="example">
        <h3>4. Mouse Events</h3>
        <div
          className={`hover-box ${isToggle ? 'active' : ''}`}
          onMouseEnter={handleMouseEnter}
          onMouseLeave={handleMouseLeave}
          style={{
            width: '200px',
            height: '100px',
            border: '2px solid #ccc',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            backgroundColor: isToggle ? '#e0e0e0' : '#f5f5f5',
            cursor: 'pointer'
          }}
        >
          Hover over me!
        </div>
        {isToggle && <p>Mouse is over the box!</p>}
      </div>

      {/* 多种事件组合 */}
      <div className="example">
        <h3>5. Multiple Events</h3>
        <div
          onClick={() => console.log('Div clicked')}
          onDoubleClick={() => console.log('Div double-clicked')}
          onMouseDown={() => console.log('Mouse down')}
          onMouseUp={() => console.log('Mouse up')}
          style={{
            width: '300px',
            height: '150px',
            border: '2px solid blue',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            backgroundColor: '#f0f8ff',
            cursor: 'pointer'
          }}
        >
          Click, double-click, or interact with this area
        </div>
      </div>

      {/* 键盘事件 */}
      <div className="example">
        <h3>6. Keyboard Events</h3>
        <div
          tabIndex={0} // 使 div 可聚焦
          onKeyDown={(e) => {
            console.log('Key pressed:', e.key);
            setMessage(`Key pressed: ${e.key}`);
          }}
          onKeyUp={(e) => {
            console.log('Key released:', e.key);
          }}
          style={{
            width: '300px',
            height: '100px',
            border: '2px solid green',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            backgroundColor: '#f0fff0'
          }}
        >
          Press any key here
        </div>
      </div>
    </div>
  );
};

2.4.2 事件对象与参数

事件对象详解:

import React, { useState } from 'react';

const EventObjectDemo = () => {
  const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
  const [keyInfo, setKeyInfo] = useState({ key: '', code: '', ctrlKey: false });
  const [formValue, setFormValue] = useState('');

  // 鼠标事件处理
  const handleMouseMove = (e) => {
    setMousePosition({
      x: e.clientX,
      y: e.clientY
    });
  };

  const handleMouseClick = (e) => {
    console.log('Mouse click event:', e);
    console.log('Target:', e.target);
    console.log('Current target:', e.currentTarget);
    console.log('Button:', e.button); // 0=左键, 1=中键, 2=右键
    console.log('Coordinates:', { x: e.clientX, y: e.clientY });
  };

  // 键盘事件处理
  const handleKeyDown = (e) => {
    setKeyInfo({
      key: e.key,
      code: e.code,
      ctrlKey: e.ctrlKey,
      shiftKey: e.shiftKey,
      altKey: e.altKey
    });

    // 阻止特定键的默认行为
    if (e.key === 'F1') {
      e.preventDefault();
      alert('F1 key is disabled');
    }
  };

  // 表单事件处理
  const handleFormChange = (e) => {
    const { name, value, type, checked } = e.target;
    
    if (type === 'checkbox') {
      setFormValue(checked);
    } else {
      setFormValue(value);
    }

    console.log('Form input change:', {
      name,
      value: type === 'checkbox' ? checked : value,
      type
    });
  };

  // 自定义事件参数传递
  const handleAction = (action, value, event) => {
    console.log('Action:', action);
    console.log('Value:', value);
    console.log('Event:', event);
    
    // 访问事件对象
    if (event) {
      event.stopPropagation(); // 阻止事件冒泡
    }
  };

  return (
    <div className="event-object-demo" onMouseMove={handleMouseMove}>
      <h2>Event Object Examples</h2>

      {/* 鼠标事件对象 */}
      <div className="example">
        <h3>1. Mouse Event Object</h3>
        <div 
          onClick={handleMouseClick}
          style={{
            width: '300px',
            height: '150px',
            border: '2px solid blue',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            backgroundColor: '#e6f3ff',
            cursor: 'pointer',
            margin: '10px 0'
          }}
        >
          Click here to see event details
        </div>
        
        <div style={{ padding: '10px', backgroundColor: '#f0f0f0' }}>
          <strong>Mouse Position:</strong> X: {mousePosition.x}, Y: {mousePosition.y}
        </div>
      </div>

      {/* 键盘事件对象 */}
      <div className="example">
        <h3>2. Keyboard Event Object</h3>
        <input
          type="text"
          onKeyDown={handleKeyDown}
          placeholder="Press any key"
          style={{ padding: '8px', width: '300px' }}
        />
        
        <div style={{ marginTop: '10px', padding: '10px', backgroundColor: '#f0f0f0' }}>
          <strong>Key Info:</strong><br />
          Key: {keyInfo.key}<br />
          Code: {keyInfo.code}<br />
          Ctrl: {keyInfo.ctrlKey ? 'Yes' : 'No'}<br />
          Shift: {keyInfo.shiftKey ? 'Yes' : 'No'}
        </div>
      </div>

      {/* 表单事件对象 */}
      <div className="example">
        <h3>3. Form Event Object</h3>
        <div>
          <label>
            Text Input:
            <input
              type="text"
              name="textInput"
              value={typeof formValue === 'string' ? formValue : ''}
              onChange={handleFormChange}
              placeholder="Type something"
              style={{ marginLeft: '10px', padding: '5px' }}
            />
          </label>
        </div>
        
        <div style={{ marginTop: '10px' }}>
          <label>
            Checkbox:
            <input
              type="checkbox"
              name="checkbox"
              checked={typeof formValue === 'boolean' ? formValue : false}
              onChange={handleFormChange}
              style={{ marginLeft: '10px' }}
            />
            Value: {formValue.toString()}
          </label>
        </div>
      </div>

      {/* 自定义参数传递 */}
      <div className="example">
        <h3>4. Custom Parameter Passing</h3>
        {['Save', 'Delete', 'Edit'].map((action) => (
          <button
            key={action}
            onClick={(e) => handleAction(action.toLowerCase(), `data-${action.toLowerCase()}`, e)}
            style={{ margin: '5px', padding: '8px 16px' }}
          >
            {action}
          </button>
        ))}
        
        <div style={{ marginTop: '10px', padding: '10px', backgroundColor: '#f0f0f0' }}>
          Check the console for event details
        </div>
      </div>

      {/* 事件冒泡演示 */}
      <div className="example">
        <h3>5. Event Bubbling</h3>
        <div
          onClick={() => console.log('Outer div clicked')}
          style={{
            width: '300px',
            height: '200px',
            border: '3px solid red',
            padding: '20px',
            backgroundColor: '#ffe6e6'
          }}
        >
          <p>Outer Div (Click me)</p>
          <div
            onClick={(e) => {
              e.stopPropagation();
              console.log('Inner div clicked (no bubbling)');
            }}
            style={{
              width: '200px',
              height: '100px',
              border: '2px solid green',
              display: 'flex',
              alignItems: 'center',
              justifyContent: 'center',
              backgroundColor: '#e6ffe6',
              cursor: 'pointer'
            }}
          >
            Inner Div (No Bubbling)
          </div>
        </div>
      </div>

      {/* 事件委托演示 */}
      <div className="example">
        <h3>6. Event Delegation</h3>
        <div
          onClick={(e) => {
            if (e.target.tagName === 'BUTTON') {
              const action = e.target.textContent;
              console.log(`Delegated action: ${action}`);
            }
          }}
          style={{ padding: '10px', border: '2px solid orange' }}
        >
          <p>Click any button (event delegation)</p>
          {['Action 1', 'Action 2', 'Action 3'].map(action => (
            <button key={action} style={{ margin: '5px' }}>
              {action}
            </button>
          ))}
        </div>
      </div>
    </div>
  );
};

2.4.3 高级事件处理模式

事件处理最佳实践:

import React, { useState, useCallback, useMemo, useRef } from 'react';

const AdvancedEventHandling = () => {
  const [items, setItems] = useState([
    { id: 1, text: 'Item 1', completed: false },
    { id: 2, text: 'Item 2', completed: true },
    { id: 3, text: 'Item 3', completed: false },
  ]);
  const [filter, setFilter] = useState('all');
  const [searchTerm, setSearchTerm] = useState('');
  const inputRef = useRef(null);

  // 使用 useCallback 优化事件处理函数
  const handleToggleItem = useCallback((id) => {
    setItems(prevItems =>
      prevItems.map(item =>
        item.id === id ? { ...item, completed: !item.completed } : item
      )
    );
  }, []);

  const handleDeleteItem = useCallback((id) => {
    setItems(prevItems => prevItems.filter(item => item.id !== id));
  }, []);

  const handleAddItem = useCallback(() => {
    if (inputRef.current && inputRef.current.value.trim()) {
      const newItem = {
        id: Date.now(),
        text: inputRef.current.value.trim(),
        completed: false
      };
      setItems(prevItems => [...prevItems, newItem]);
      inputRef.current.value = '';
      inputRef.current.focus();
    }
  }, []);

  const handleKeyPress = useCallback((e) => {
    if (e.key === 'Enter') {
      handleAddItem();
    }
  }, [handleAddItem]);

  // 使用 useMemo 优化计算
  const filteredItems = useMemo(() => {
    return items.filter(item => {
      const matchesFilter = 
        filter === 'all' ||
        (filter === 'completed' && item.completed) ||
        (filter === 'active' && !item.completed);
      
      const matchesSearch = item.text
        .toLowerCase()
        .includes(searchTerm.toLowerCase());
      
      return matchesFilter && matchesSearch;
    });
  }, [items, filter, searchTerm]);

  const stats = useMemo(() => ({
    total: items.length,
    completed: items.filter(item => item.completed).length,
    active: items.filter(item => !item.completed).length
  }), [items]);

  // 防抖搜索
  const debouncedSearch = useCallback(
    debounce((value) => {
      setSearchTerm(value);
    }, 300),
    []
  );

  return (
    <div className="advanced-event-handling">
      <h2>Advanced Event Handling</h2>

      {/* 统计信息 */}
      <div className="stats">
        <span>Total: {stats.total}</span>
        <span>Active: {stats.active}</span>
        <span>Completed: {stats.completed}</span>
      </div>

      {/* 添加项目 */}
      <div className="add-item">
        <input
          ref={inputRef}
          type="text"
          onKeyPress={handleKeyPress}
          placeholder="Add new item..."
        />
        <button onClick={handleAddItem}>Add</button>
      </div>

      {/* 搜索和过滤 */}
      <div className="filters">
        <input
          type="text"
          onChange={(e) => debouncedSearch(e.target.value)}
          placeholder="Search items..."
        />
        
        <div className="filter-buttons">
          {['all', 'active', 'completed'].map(filterType => (
            <button
              key={filterType}
              onClick={() => setFilter(filterType)}
              className={filter === filterType ? 'active' : ''}
            >
              {filterType.charAt(0).toUpperCase() + filterType.slice(1)}
            </button>
          ))}
        </div>
      </div>

      {/* 项目列表 */}
      <div className="item-list">
        {filteredItems.map(item => (
          <TodoItem
            key={item.id}
            item={item}
            onToggle={handleToggleItem}
            onDelete={handleDeleteItem}
          />
        ))}
      </div>

      {/* 批量操作 */}
      <div className="bulk-actions">
        <button onClick={() => setItems(items.map(item => ({ ...item, completed: true })))}>
          Complete All
        </button>
        <button onClick={() => setItems(items.map(item => ({ ...item, completed: false })))}>
          Reset All
        </button>
        <button onClick={() => setItems(items.filter(item => !item.completed))}>
          Clear Completed
        </button>
      </div>
    </div>
  );
};

// TodoItem 组件
const TodoItem = React.memo(({ item, onToggle, onDelete }) => {
  const handleToggle = useCallback(() => {
    onToggle(item.id);
  }, [item.id, onToggle]);

  const handleDelete = useCallback(() => {
    onDelete(item.id);
  }, [item.id, onDelete]);

  return (
    <div 
      className={`todo-item ${item.completed ? 'completed' : 'active'}`}
      style={{
        display: 'flex',
        alignItems: 'center',
        padding: '8px',
        margin: '4px 0',
        backgroundColor: item.completed ? '#f0f0f0' : 'white',
        border: '1px solid #ddd',
        borderRadius: '4px'
      }}
    >
      <input
        type="checkbox"
        checked={item.completed}
        onChange={handleToggle}
        style={{ marginRight: '8px' }}
      />
      <span 
        style={{
          textDecoration: item.completed ? 'line-through' : 'none',
          flex: 1,
          color: item.completed ? '#888' : '#000'
        }}
      >
        {item.text}
      </span>
      <button
        onClick={handleDelete}
        style={{
          padding: '4px 8px',
          backgroundColor: '#ff4444',
          color: 'white',
          border: 'none',
          borderRadius: '4px',
          cursor: 'pointer'
        }}
      >
        Delete
      </button>
    </div>
  );
});

// 防抖函数
function debounce(func, wait) {
  let timeout;
  return function executedFunction(...args) {
    const later = () => {
      clearTimeout(timeout);
      func(...args);
    };
    clearTimeout(timeout);
    timeout = setTimeout(later, wait);
  };
}

export default AdvancedEventHandling;

2.5 JSX 样式与 CSS

2.5.1 内联样式

内联样式基础:

import React, { useState } from 'react';

const InlineStyles = () => {
  const [isHovered, setIsHovered] = useState(false);
  const [theme, setTheme] = useState('light');

  // 样式对象定义
  const containerStyle = {
    maxWidth: '800px',
    margin: '0 auto',
    padding: '20px',
    backgroundColor: theme === 'light' ? '#ffffff' : '#1a1a1a',
    color: theme === 'light' ? '#000000' : '#ffffff',
    borderRadius: '8px',
    boxShadow: '0 2px 10px rgba(0, 0, 0, 0.1)'
  };

  const buttonStyle = {
    padding: '12px 24px',
    margin: '5px',
    border: 'none',
    borderRadius: '6px',
    fontSize: '16px',
    cursor: 'pointer',
    transition: 'all 0.3s ease',
    backgroundColor: '#007bff',
    color: '#ffffff'
  };

  const hoverButtonStyle = {
    ...buttonStyle,
    backgroundColor: isHovered ? '#0056b3' : '#007bff',
    transform: isHovered ? 'translateY(-2px)' : 'translateY(0)',
    boxShadow: isHovered ? '0 4px 12px rgba(0, 123, 255, 0.3)' : 'none'
  };

  const textStyle = {
    fontSize: '18px',
    lineHeight: '1.6',
    marginBottom: '20px',
    fontFamily: 'Arial, sans-serif'
  };

  const cardStyle = {
    backgroundColor: theme === 'light' ? '#f8f9fa' : '#2d2d2d',
    padding: '20px',
    margin: '15px 0',
    borderRadius: '8px',
    border: `1px solid ${theme === 'light' ? '#dee2e6' : '#404040'}`,
    transition: 'transform 0.2s ease'
  };

  return (
    <div style={containerStyle}>
      <h1 style={{ textAlign: 'center', marginBottom: '30px' }}>
        Inline Styles Example
      </h1>

      {/* 基础内联样式 */}
      <div>
        <p style={textStyle}>
          This paragraph uses inline styling with a style object.
        </p>
        
        {/* 动态样式 */}
        <div style={cardStyle}>
          <h3>Dynamic Styling</h3>
          <p>Theme: {theme}</p>
          <button 
            onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}
            style={buttonStyle}
          >
            Toggle Theme
          </button>
        </div>

        {/* 交互样式 */}
        <div style={cardStyle}>
          <h3>Interactive Styling</h3>
          <div
            style={hoverButtonStyle}
            onMouseEnter={() => setIsHovered(true)}
            onMouseLeave={() => setIsHovered(false)}
          >
            Hover over me!
          </div>
        </div>

        {/* 条件样式 */}
        <div style={cardStyle}>
          <h3>Conditional Styling</h3>
          {['Success', 'Warning', 'Error', 'Info'].map((type) => {
            const colors = {
              Success: { bg: '#d4edda', color: '#155724' },
              Warning: { bg: '#fff3cd', color: '#856404' },
              Error: { bg: '#f8d7da', color: '#721c24' },
              Info: { bg: '#d1ecf1', color: '#0c5460' }
            };

            return (
              <div
                key={type}
                style={{
                  backgroundColor: colors[type].bg,
                  color: colors[type].color,
                  padding: '10px',
                  margin: '5px 0',
                  borderRadius: '4px',
                  border: `1px solid ${colors[type].color}`
                }}
              >
                {type} Message
              </div>
            );
          })}
        </div>

        {/* 计算样式 */}
        <div style={cardStyle}>
          <h3>Computed Styles</h3>
          {[1, 2, 3, 4, 5].map((num) => (
            <div
              key={num}
              style={{
                width: `${num * 20}px`,
                height: '30px',
                backgroundColor: `hsl(${num * 60}, 70%, 50%)`,
                margin: '5px',
                borderRadius: '4px',
                display: 'inline-block',
                textAlign: 'center',
                lineHeight: '30px',
                color: 'white',
                fontWeight: 'bold'
              }}
            >
              {num}
            </div>
          ))}
        </div>

        {/* 响应式样式模拟 */}
        <div style={cardStyle}>
          <h3>Responsive Styles (simulated)</h3>
          <div
            style={{
              display: 'grid',
              gridTemplateColumns: window.innerWidth > 768 ? 'repeat(3, 1fr)' : '1fr',
              gap: '10px'
            }}
          >
            {['Card 1', 'Card 2', 'Card 3'].map((text, index) => (
              <div
                key={index}
                style={{
                  padding: '20px',
                  backgroundColor: theme === 'light' ? '#e9ecef' : '#404040',
                  borderRadius: '8px',
                  textAlign: 'center'
                }}
              >
                {text}
              </div>
            ))}
          </div>
        </div>
      </div>
    </div>
  );
};

2.5.2 CSS 模块化

CSS Modules 使用:

// styles/Component.module.css
.container {
  max-width: 800px;
  margin: 0 auto;
  padding: 20px;
  background-color: #ffffff;
  border-radius: 8px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}

.title {
  color: #2c3e50;
  font-size: 2rem;
  text-align: center;
  margin-bottom: 2rem;
}

.button {
  padding: 12px 24px;
  border: none;
  border-radius: 6px;
  font-size: 16px;
  cursor: pointer;
  transition: all 0.3s ease;
}

.buttonPrimary {
  background-color: #007bff;
  color: white;
}

.buttonPrimary:hover {
  background-color: #0056b3;
  transform: translateY(-2px);
}

.buttonSecondary {
  background-color: #6c757d;
  color: white;
}

.buttonSecondary:hover {
  background-color: #5a6268;
}

.card {
  background-color: #f8f9fa;
  padding: 20px;
  margin: 15px 0;
  border-radius: 8px;
  border: 1px solid #dee2e6;
}

.cardDark {
  background-color: #2d2d2d;
  border-color: #404040;
  color: white;
}

/* 组合选择器 */
.container :global(.global-class) {
  color: red;
}

/* 变量 */
:root {
  --primary-color: #007bff;
  --secondary-color: #6c757d;
  --success-color: #28a745;
  --warning-color: #ffc107;
  --error-color: #dc3545;
}
// components/StyledComponent.jsx
import React, { useState } from 'react';
import styles from '../styles/Component.module.css';

const StyledComponent = () => {
  const [theme, setTheme] = useState('light');
  const [isActive, setIsActive] = useState(false);

  return (
    <div className={styles.container}>
      <h1 className={styles.title}>CSS Modules Example</h1>

      {/* 基础样式使用 */}
      <div className={styles.card}>
        <h3>Basic CSS Modules</h3>
        <p>This card uses CSS Modules for styling.</p>
        <button className={styles.button}>
          Basic Button
        </button>
      </div>

      {/* 样式组合 */}
      <div className={styles.card}>
        <h3>Style Composition</h3>
        <button className={`${styles.button} ${styles.buttonPrimary}`}>
          Primary Button
        </button>
        <button className={`${styles.button} ${styles.buttonSecondary}`}>
          Secondary Button
        </button>
      </div>

      {/* 条件样式 */}
      <div className={`${styles.card} ${theme === 'dark' ? styles.cardDark : ''}`}>
        <h3>Conditional Styling</h3>
        <p>Current theme: {theme}</p>
        <button 
          className={`${styles.button} ${styles.buttonPrimary}`}
          onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}
        >
          Toggle Theme
        </button>
      </div>

      {/* 动态样式类 */}
      <div className={styles.card}>
        <h3>Dynamic Classes</h3>
        <button
          className={`${styles.button} ${isActive ? styles.buttonPrimary : styles.buttonSecondary}`}
          onClick={() => setIsActive(!isActive)}
        >
          {isActive ? 'Active' : 'Inactive'}
        </button>
      </div>

      {/* 使用 CSS 变量 */}
      <div 
        className={styles.card}
        style={{
          '--primary-color': '#ff6b6b',
          '--secondary-color': '#4ecdc4'
        }}
      >
        <h3>CSS Variables</h3>
        <div 
          className={styles.button}
          style={{
            backgroundColor: 'var(--primary-color)',
            color: 'white'
          }}
        >
          Custom Primary
        </div>
        <div 
          className={styles.button}
          style={{
            backgroundColor: 'var(--secondary-color)',
            color: 'white'
          }}
        >
          Custom Secondary
        </div>
      </div>

      {/* 响应式样式 */}
      <div className={`${styles.card} ${styles.responsiveCard}`}>
        <h3>Responsive Design</h3>
        <p>This card adapts to different screen sizes.</p>
      </div>
    </div>
  );
};

export default StyledComponent;

2.5.3 CSS-in-JS 方案

使用 styled-components:

import React, { useState } from 'react';
import styled, { css, ThemeProvider, createGlobalStyle } from 'styled-components';

// 全局样式
const GlobalStyle = createGlobalStyle`
  * {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
  }

  body {
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    background-color: ${props => props.theme.backgroundColor};
    color: ${props => props.theme.textColor};
    transition: all 0.3s ease;
  }
`;

// 主题定义
const lightTheme = {
  backgroundColor: '#ffffff',
  textColor: '#333333',
  primaryColor: '#007bff',
  secondaryColor: '#6c757d',
  successColor: '#28a745',
  warningColor: '#ffc107',
  errorColor: '#dc3545',
  borderRadius: '8px',
  boxShadow: '0 2px 10px rgba(0, 0, 0, 0.1)'
};

const darkTheme = {
  backgroundColor: '#1a1a1a',
  textColor: '#ffffff',
  primaryColor: '#0d6efd',
  secondaryColor: '#6c757d',
  successColor: '#198754',
  warningColor: '#ffc107',
  errorColor: '#dc3545',
  borderRadius: '8px',
  boxShadow: '0 2px 10px rgba(255, 255, 255, 0.1)'
};

// 样式组件定义
const Container = styled.div`
  max-width: 800px;
  margin: 0 auto;
  padding: 2rem;
  background-color: ${props => props.theme.backgroundColor};
  border-radius: ${props => props.theme.borderRadius};
  box-shadow: ${props => props.theme.boxShadow};
`;

const Title = styled.h1`
  color: ${props => props.theme.textColor};
  font-size: 2rem;
  text-align: center;
  margin-bottom: 2rem;
  font-weight: 600;
`;

const Button = styled.button`
  padding: 12px 24px;
  border: none;
  border-radius: 6px;
  font-size: 16px;
  cursor: pointer;
  transition: all 0.3s ease;
  font-weight: 500;
  
  ${props => {
    switch (props.variant) {
      case 'primary':
        return css`
          background-color: ${props.theme.primaryColor};
          color: white;
          
          &:hover {
            background-color: ${props.theme.primaryColor}dd;
            transform: translateY(-2px);
            box-shadow: 0 4px 12px ${props.theme.primaryColor}66;
          }
        `;
      case 'secondary':
        return css`
          background-color: ${props.theme.secondaryColor};
          color: white;
          
          &:hover {
            background-color: ${props.theme.secondaryColor}dd;
          }
        `;
      case 'success':
        return css`
          background-color: ${props.theme.successColor};
          color: white;
          
          &:hover {
            background-color: ${props.theme.successColor}dd;
          }
        `;
      case 'warning':
        return css`
          background-color: ${props.theme.warningColor};
          color: black;
          
          &:hover {
            background-color: ${props.theme.warningColor}dd;
          }
        `;
      case 'error':
        return css`
          background-color: ${props.theme.errorColor};
          color: white;
          
          &:hover {
            background-color: ${props.theme.errorColor}dd;
          }
        `;
      default:
        return css`
          background-color: ${props.theme.primaryColor};
          color: white;
        `;
    }
  }}
  
  ${props => props.size === 'small' && css`
    padding: 8px 16px;
    font-size: 14px;
  `}
  
  ${props => props.size === 'large' && css`
    padding: 16px 32px;
    font-size: 18px;
  `}
  
  ${props => props.fullWidth && css`
    width: 100%;
    display: block;
  `}
  
  &:disabled {
    opacity: 0.6;
    cursor: not-allowed;
    transform: none !important;
  }
`;

const Card = styled.div`
  background-color: ${props => props.theme === 'dark' ? '#2d2d2d' : '#f8f9fa'};
  padding: 20px;
  margin: 15px 0;
  border-radius: ${props => props.theme.borderRadius};
  border: 1px solid ${props => props.theme === 'dark' ? '#404040' : '#dee2e6'};
  transition: all 0.3s ease;
  
  &:hover {
    transform: translateY(-2px);
    box-shadow: ${props => props.theme === 'dark' 
      ? '0 4px 20px rgba(255, 255, 255, 0.1)' 
      : '0 4px 20px rgba(0, 0, 0, 0.1)'
    };
  }
`;

const Input = styled.input`
  width: 100%;
  padding: 12px;
  border: 2px solid ${props => props.theme === 'dark' ? '#404040' : '#dee2e6'};
  border-radius: 6px;
  font-size: 16px;
  background-color: ${props => props.theme === 'dark' ? '#1a1a1a' : 'white'};
  color: ${props => props.theme === 'dark' ? 'white' : 'black'};
  transition: all 0.3s ease;
  
  &:focus {
    outline: none;
    border-color: ${props => props.theme.primaryColor};
    box-shadow: 0 0 0 3px ${props => props.theme.primaryColor}33;
  }
  
  &::placeholder {
    color: ${props => props.theme === 'dark' ? '#888' : '#999'};
  }
`;

const Grid = styled.div`
  display: grid;
  gap: 20px;
  margin: 20px 0;
  
  ${props => props.columns === 2 && css`
    grid-template-columns: repeat(2, 1fr);
  `}
  
  ${props => props.columns === 3 && css`
    grid-template-columns: repeat(3, 1fr);
  `}
  
  ${props => props.columns === 4 && css`
    grid-template-columns: repeat(4, 1fr);
  `}
  
  @media (max-width: 768px) {
    grid-template-columns: 1fr;
  }
`;

const CSSInJSExample = () => {
  const [theme, setTheme] = useState('light');
  const [inputValue, setInputValue] = useState('');
  const [isLoading, setIsLoading] = useState(false);

  const currentTheme = theme === 'light' ? lightTheme : darkTheme;

  return (
    <ThemeProvider theme={currentTheme}>
      <GlobalStyle />
      <Container>
        <Title>CSS-in-JS with Styled Components</Title>

        {/* 主题切换 */}
        <Card>
          <h3>Theme Switching</h3>
          <p>Current theme: {theme}</p>
          <Button 
            variant="primary"
            onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}
          >
            Toggle Theme
          </Button>
        </Card>

        {/* 按钮变体 */}
        <Card>
          <h3>Button Variants</h3>
          <Grid columns={3}>
            <Button variant="primary">Primary</Button>
            <Button variant="secondary">Secondary</Button>
            <Button variant="success">Success</Button>
            <Button variant="warning">Warning</Button>
            <Button variant="error">Error</Button>
            <Button 
              variant="primary" 
              disabled={isLoading}
              onClick={() => {
                setIsLoading(true);
                setTimeout(() => setIsLoading(false), 2000);
              }}
            >
              {isLoading ? 'Loading...' : 'Click Me'}
            </Button>
          </Grid>
        </Card>

        {/* 按钮尺寸 */}
        <Card>
          <h3>Button Sizes</h3>
          <div style={{ display: 'flex', gap: '10px', alignItems: 'center' }}>
            <Button variant="primary" size="small">Small</Button>
            <Button variant="primary">Medium</Button>
            <Button variant="primary" size="large">Large</Button>
            <Button variant="primary" fullWidth>Full Width</Button>
          </div>
        </Card>

        {/* 表单元素 */}
        <Card>
          <h3>Form Elements</h3>
          <Input
            type="text"
            placeholder="Type something..."
            value={inputValue}
            onChange={(e) => setInputValue(e.target.value)}
          />
          <p style={{ marginTop: '10px' }}>Input value: {inputValue}</p>
        </Card>

        {/* 动态样式 */}
        <Card>
          <h3>Dynamic Styling</h3>
          {[1, 2, 3, 4, 5].map((num) => (
            <Button
              key={num}
              variant="primary"
              style={{
                margin: '5px',
                backgroundColor: `hsl(${num * 60}, 70%, 50%)`,
                borderColor: `hsl(${num * 60}, 70%, 40%)`
              }}
            >
              Color {num}
            </Button>
          ))}
        </Card>

        {/* 响应式网格 */}
        <Card>
          <h3>Responsive Grid</h3>
          <Grid columns={3}>
            {['Card 1', 'Card 2', 'Card 3', 'Card 4', 'Card 5', 'Card 6'].map((text) => (
              <Card key={text}>
                <h4>{text}</h4>
                <p>Responsive card content</p>
              </Card>
            ))}
          </Grid>
        </Card>
      </Container>
    </ThemeProvider>
  );
};

export default CSSInJSExample;

通过本章的 JSX 语法详解,你已经掌握了 React 开发的核心语法,包括 JSX 基础概念、表达式插值、事件处理、样式管理等关键内容。这些知识是构建 React 应用的基础,下一章我们将深入学习 React 组件系统。

posted @ 2025-11-29 18:15  seven3306  阅读(1)  评论(0)    收藏  举报