React 入门文档
基础概念
组件
一个方法就是一个React组件,组件是搭建应用的最基本单位,React的哲学是用一个一个组件组装成一个大的应用。例如下面的商品列表应用的组件结构图

JSX
jsx 是一种可以让你在js文件里用类似写 html 标签的方式写组件的语法。可以把他当作html标签来写。
React 组件必须以大写字母开头,这是为了区分组件与 html 标签
用className 来指定css类名,因为 class 是 js 关键字,在 js 文件中不能使用
export default function MyApp() {
return (
<div>
<h1>欢迎来到我的应用</h1>
<MyButton className="css-class-name" />
</div>
);
}
当需要在 JSX 中插入动态JS内容时需要用一对大括号 {} 来把内容括起来。{}内代码可以执行任意 js 表达式,注意只能是表达式,不能写语句。
React 没有引入特殊的语法来实现分支判断,循环等控制逻辑,而是完全利用 js 来做控制,你可以直接在 js 中通过控制生成目标 jsx(方式一) ,或者在 jsx 中用{}引入 js 语法来做逻辑控制(方式二)。在处理循环时需要注意一个特殊属性 key
// 方式 1
let content;
if (isLoggedIn) {
content = <AdminPanel />;
} else {
content = <LoginForm />;
}
return (
<div>
{content}
</div>
);
//============================================
// 方式2
<div>
{isLoggedIn ? (
<AdminPanel />
) : (
<LoginForm />
)}
</div>
//==============================================
// 列表循环
const products = [
{ title: 'Cabbage', id: 1 },
{ title: 'Garlic', id: 2 },
{ title: 'Apple', id: 3 },
];
const listItems = products.map(product => {
// 注意这里的key, 必须是这个列表内的唯一值。用于在列表内区分各个子组件,在列表数据源更新后,React 通过 key 来识别新旧组件是否是同一组件,应该替换还是更新它。
return (
<li key={product.id}>
{product.title}
</li>
)}
);
return (
<ul>{listItems}</ul>
);
事件处理
在组件上用 onXxx={handlerFn} 来绑定事件处理函数,注意 handlerFn 结尾处没有(),不要调用事件处理函数,React 会在事件触发时,调用这个函数。函数被调用时会有一个参数 event,这个event 就是浏览器的事件对象。
function MyButton() {
function handleClick(event) {
console.log(event)
alert('You clicked me!');
}
return (
<button onClick={handleClick}>
点我
</button>
);
}
form表单
对于原生html标签,比如表单元素 input,可以看作内置的组件来使用,它也可以通过 prop 来被父组件管理,与父组件通讯。
import { useState } from 'react'
export default function MyApp() {
const [someValue, setValue] = useState('0')
function onChange(event) {
const v = Number(event.target.value)
if(isNaN(v) || v < 0) return // 如果键盘输入内容不是大于等于0的数字就拦截输入
// 只有符合要求的输入才回显到输入框
setValue(event.target.value)
}
return (
<input value={someValue} onChange={onChange} />
)
内部状态
状态是组件内部的记忆,用于记住这个组件经过了哪些交互,反映当前状态到用户界面上。比如记录一个按钮被点击了几次。
// 首先,从 React 引入 useState
import { useState } from 'react';
function MyButton() {
// 声明一个状态变量和更新状态的函数
const [count, setCount] = useState(0);
function handleClick() {
// 调用更新函数更新状态值
setCount(count + 1);
}
return (
<button onClick={handleClick}>
Clicked {count} times
</button>
);
}
更新函数被调用后,React 会再次调用组件函数,渲染新的用户界面。
同一个组件的不同实例之间的状态是独立的,不会相互影响。
副作用
有时候应用需要与外部系统进行一些交互操作,比如上报数据,获取数据,控制非 React 组件之类的操作。这类操作不属于 React 内部的从状态到界面渲染的流程。React 把渲染页面之外的操作都称为 副作用(Effect) ,React 提供 useEffect 函数来执行这类操作,Effect 在渲染后执行。
useEffect 可以添加副作用,执行清理函数来清理副作用遗留下来的资源,还可以指定这个副作用什么时候执行。
这里来以一个聊天服务来举例
import { useState, useEffect } from 'react';
import { createConnection } from './chat.js';
export default function ChatRoom() {
// 添加副作用,第一个参数是副作用函数,第二个参数是依赖数组,用于决定副作用函数在什么时候执行
useEffect(() => {
// 这里可以与外部服务进行交互
const connection = createConnection();
connection.connect();
// 这里返回一个清理函数,在组件卸载或者需要再次执行这个副作用之前执行,用于清理上次执行的副作用遗留资源
return () => connection.disconnect();
}, []); // useEffect 的第二个参数,依赖数组,当依赖数组内元素变化时,React 会再次执行这个副作用。当依赖数组是空时,只有在组件挂载时执行一次,组件卸载时执行一次清理函数。
return <h1>欢迎前来聊天!</h1>;
}
组件接收外部数据
组件肯定不能只有内部状态,还需要从外部接收数据,通知(调用)外部父级组件。React 组件与外部通信的主要渠道是通过 prop。 prop 是一个对象,由 React 执行组件函数时传递给函数,prop 内可以包含任何合法的 js 值。
import { useState } from 'react';
export default function MyApp() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
function handleChange() {
// 其他一些逻辑
}
return (
<div>
<h1>共同更新的计数器</h1>
{/* 父组件传递 prop 到子组件的方式 */}
<MyButton count={count} onClick={handleClick} />
{/* 另一个组件,有不同的prop*/}
<OtherComponent theme="dark" onChange={handleChange} />
</div>
);
}
function MyButton(props) {
// 通过 prop 接收父级组件传过来的数据与方法,count 是数据用于显示, onClick 是方法用于通知(调用)父组件
const { count, onClick } = props
return (
<button onClick={onClick}>
点了 {count} 次
</button>
);
}
小结
React 的基础是组件,组件就是普通的函数,这个函数需要返回要渲染的 html 标签或者其他自定义组件。用JSX调用组件,通过 onXxx 属性绑定浏览器事件处理函数,然后用 useState 来保存和更新内部状态,用 prop 来与外部进行通讯,用Effect来与 React 外部环境进行交互。掌握了这些基础的概念就可以进行大多数的 React 应用开发了,任何复杂的应用都可以用 组件 ,事件,状态,属性,Effect 组装出来。
运行起来
这里以 vite 作为构建工具进行演示。用以下命令进行项目的初始化
npm create vite@latest my-app -- --template react
前端的基础 html 文件是必不可少的,最简单的 index.html 文件如下
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>my-app</title>
</head>
<body>
<!-- 非常重要,入口容器,React 应用渲染到这里-->
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
main.jsx 说明
// 引入React依赖
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.jsx' // 这里是我们写好的组件
// 获取入口容器,然后创建根应用,进行渲染。后续React就会接管dom的更新和渲染,App组件内部的任何更新都会即时渲染到dom上,保持用户界面实时更新。
// StrictMode 是 React 内置组件,用于在开发期帮助发现副作用问题
createRoot(document.getElementById('root')).render(
<StrictMode>
<App />
</StrictMode>,
)
通过 createRoot 来创建根应用,调用根应用的 render 方法将我们的组件渲染到根应用上。我们写的组件就会渲染到浏览器上,并可以接收浏览器交互事件,更新组件内部状态,再由 React 把内部状态实时渲染到浏览器,整个应用就运行起来了。