使用 JSX 做更好的关注点分离
约 293 个字 138 行代码 预计阅读时间 3 分钟
一、什么是 JSX
JSX 是 JS 的语法扩展,用来在 JS 文件中通过 类 HTML 标签表达页面的视图与交互逻辑
| HTML |
|---|
| <div className="container">
<CustomComponent onClick={() => alert("hello")}>
Hello {props.name}!
</CustomComponent>
</div>
|
二、React 组件
返回 JSX 的函数就是 React 最简单的组件,可以和 HTML 标签一样嵌套使用。React 使用 props 参数向组件传递数据,提升组件的复用性。
| TypeScript |
|---|
| import React from "react";
import "./Button.css";
export type ButtonProps = {
text: string;
onClick?: () => void;
variant?: "primary" | "secondary" | "danger"; // 增加样式变体
};
const Button: React.FC<ButtonProps> = ({
text,
onClick,
variant = "secondary", // 默认为次要按钮
}) => {
return (
<button className={`btn btn-${variant}`} onClick={onClick}>
{text}
</button>
);
};
export default Button;
|
在使用组件的时候,通过其标签的属性组装成 props 对象,传递给组件,语法和 HTML attribute 类似,但是值可以是任意的 JS 对象
| TypeScript |
|---|
| import React from "react";
import Button from "./Button";
import "./Dialog.css"; // 引入 CSS
type DialogProps = {
isOpen: boolean; // 控制弹窗显示/隐藏的核心属性
title: string;
onClose: () => void; // 关闭时的回调
onConfirm?: () => void; // 确认时的回调
children?: React.ReactNode; // 弹窗内容
};
const Dialog: React.FC<DialogProps> = ({
isOpen,
title,
onClose,
onConfirm,
children,
}) => {
// 如果 isOpen 为 false,直接返回 null,什么都不渲染
if (!isOpen) return null;
return (
// 1. 遮罩层 (Overlay):点击背景通常也能关闭
<div className="dialog-overlay" onClick={onClose}>
{/* 2. 弹窗主体:阻止冒泡,防止点击弹窗内容时触发遮罩层的关闭事件 */}
<div
className="dialog-content"
onClick={(e) => e.stopPropagation()}
>
{/* 标题栏 */}
<div className="dialog-header">
<h3 className="dialog-title">{title}</h3>
<button className="dialog-close-icon" onClick={onClose}>
×
</button>
</div>
{/* 内容区域 */}
<div className="dialog-body">{children}</div>
{/* 底部按钮栏 */}
<div className="dialog-footer">
<Button text="取消" onClick={onClose} variant="secondary" />
<Button
text="确认"
onClick={() => {
if (onConfirm) onConfirm();
onClose(); // 确认后通常也自动关闭,看需求
}}
variant="primary"
/>
</div>
</div>
</div>
);
};
export default Dialog;
|
| TypeScript |
|---|
| import { useState } from "react";
import Button from "./Button";
import Dialog from "./Dialog";
const App = () => {
// 1. 定义状态控制弹窗显示
const [isDialogOpen, setIsDialogOpen] = useState(false);
const handleConfirm = () => {
alert("操作已确认!");
// 这里可以执行 API 请求等操作
};
return (
<div style={{ padding: 50 }}>
<h1>React Dialog Demo</h1>
{/* 点击按钮打开弹窗 */}
<Button
text="打开弹窗"
onClick={() => setIsDialogOpen(true)}
variant="primary"
/>
{/* Dialog 组件 */}
<Dialog
isOpen={isDialogOpen}
title="删除确认"
onClose={() => setIsDialogOpen(false)} // 传递关闭函数
onConfirm={handleConfirm}
>
<p>确定要删除这个项目吗?</p>
<p style={{ color: "red", fontSize: "12px" }}>
此操作无法撤销。
</p>
</Dialog>
</div>
);
};
export default App;
|
组件写好后通过 reac-dom 渲染到页面
| TypeScript |
|---|
| import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App.tsx";
import "./index.css";
createRoot(document.getElementById("root")!).render(
<StrictMode>
{/* <App /> */}
<App />
</StrictMode>
);
|
三、JSX 规则
React 组件规定:
-
组件名称使用 Pascal 风格(首字母大写)
-
组件仅接收 props 一个参数,用来暴露可配置属性,其子组件被 React 通过 children 属性注入
-
在组件内部 props 是只读的,不允许对其进行修改
JSX 语法规则:
-
必须有根节点
-
所有标签需要闭合
-
和 HTML 属性差异
-
自动转移 content
-
JavaScript 表达式用 {} 包裹
-
内联样式 (style) 接受对象,而非字符串
-
JSX 中的注释写法