React Tutorial
https://www.youtube.com/watch?v=SqcY0GlETPk
Vite
Vite (French word for "quick", pronounced /vit/
, like "veet") is a new breed of frontend build tooling that significantly improves the frontend development experience.
File Structure
├── public/ # 静态资源目录
│ ├── favicon.ico # 网站图标
│ └── robots.txt # 搜索引擎爬虫配置文件
│
├── src/ # 源代码目录
│ ├── assets/ # 资源文件目录
│ │ ├── images/ # 图片资源
│ │ └── styles/ # 样式文件
│ │
│ ├── components/ # 可复用组件目录
│ │ ├── Button/
│ │ └── Header/
│ │
│ ├── hooks/ # 自定义 React Hooks
│ ├── layouts/ # 布局组件
│ ├── pages/ # 页面组件
│ ├── services/ # API 服务
│ ├── store/ # 状态管理(如 Redux)
│ ├── utils/ # 工具函数
│ ├── App.tsx # 应用根组件
│ ├── main.tsx # 应用入口文件
│ └── vite-env.d.ts # Vite 类型声明文件
│
├── .gitignore # Git 忽略文件配置
├── index.html # HTML 模板
├── package.json # 项目依赖配置
├── tsconfig.json # TypeScript 配置
├── vite.config.ts # Vite 配置文件
└── README.md # 项目说明文档
- `tsconfig.json`: telling TypeScript compiler how to compile code to javascript.
- `.tsx`: `ts` for TypeScript, `x` for react component.
- Using PascalCasing for react component, html tag is always lowercase.
public/
- 存放不需要经过打包的静态资源
- 可以通过绝对路径直接访问
src/assets/
- 存放需要经过打包处理的资源文件
- 包括图片、样式等
src/components/
- 存放可复用的 React 组件
- 每个组件通常包含自己的样式和测试文件
src/hooks/
- 存放自定义 React Hooks
- 用于提取可复用的状态逻辑
src/layouts/
- 存放页面布局组件
- 如页面框架、导航栏等
src/pages/
- 存放页面级组件
- 通常与路由配置对应
src/services/
- 存放 API 接口封装
- 处理与后端的数据交互
src/store/
- 状态管理相关文件
- 如 Redux、Zustand 等的配置和状态定义
src/utils/
- 存放工具函数
- 常用的辅助方法
配置文件
vite.config.ts
:Vite 的配置文件tsconfig.json
:TypeScript 配置package.json
:项目依赖和脚本配置index.html
:应用的 HTML 模板
入口文件
src/main.tsx
:应用的入口文件src/App.tsx
:根组件
建议
- 根据项目规模和需求适当调整目录结构
- 保持目录结构清晰,便于维护
- 可以添加
types/
目录存放类型定义 - 可以添加
constants/
目录存放常量 - 大型项目可以考虑按功能模块划分目录
Dom Render
- Virtual DOM is data structure and properties in javascript
- When the state changed, it will compare with the prev virtual DOM.
- Then update the actual DOM(This is done by react-dom package).
Demo
Create Vite with React demo project.
npm create vite@4.1.0
cd foo
npm i
npm run dev
Install Bootstrap
npm i bootstrap@5.2.3
CSS
index.css
: global css, used byindex.js
.app.css
: component css, used byApp.js
and child components.
StrictMode
- It will send 2 requests after render to find potential problems
- Static check.
Prettier
Install code format plugin Prettier
cmd+shift+p => format document => configure default formatter
It can also format document by click right mouse.
Component
In function, it only can return one element.
function ListGroup() {
return (
// <h1>List</h1> error here
// its something like: React.createElement('h1')
<ul className="list-group">
<li className="list-group-item">An item</li>
</ul>
);
}
The solution is to wrap multi elements by div
tags.
cmd+shit+p => wrap with abbreviation => div
Fragment
Use div
wrap will add extra useless tag, use Fragment
instead.
function ListGroup() {
return (
<Fragment>
<h1>List</h1>
<ul className="list-group">
<li className="list-group-item">An item</li>
</ul>
</Fragment>
);
}
Equivalent to <>
function ListGroup() {
return (
<>
<h1>List</h1>
<ul className="list-group">
<li className="list-group-item">An item</li>
</ul>
</>
);
}
Key Prop
Every child in a list should have a unique key
prop. React needs it to track each item.
function ListGroup() {
const items = ["New York", "San Francisco", "Tokyo", "London", "Paris"];
return (
<div>
<h1>List</h1>
<ul className="list-group">
{items.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
</div>
);
}
Chrome
Install React Developer Tools
https://react.dev/learn/react-developer-tools
Conditional Rendering
When use &&
in expression, if first args is true, the whole exp value will be the second arg.
function ListGroup() {
const items = [];
return (
<div>
<h1>List</h1>
{items.length === 0 && <p>No item found</p>}
<ul className="list-group">
{items.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
</div>
);
}
Equivalent to
{items.length === 0 ? <p>No item found</p> : null}
function ListGroup() {
const items = [];
const message = items.length === 0 ? <p>No item found</p> : null
return (
<div>
<h1>List</h1>
{message}
<ul className="list-group">
{items.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
</div>
);
}
Use Function
function ListGroup() {
const items = [];
const getMessage = () => {
return items.length === 0 ? <p>No item found</p> : null;
}
return (
<div>
<h1>List</h1>
{getMessage()}
<ul className="list-group">
{items.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
</div>
);
}
Handle Events
OnClick Prop, index
and event
args is optional.
function ListGroup() {
const items = ["New York", "San Francisco", "Tokyo", "London", "Paris"];
return (
<div>
<h1>List</h1>
{items.length === 0 && <p>No item found</p>}
<ul className="list-group">
{items.map((item, index) => (
<li
className="list-group-item"
key={item}
onClick={(event) => console.log(item, index, event)}
>
{item}
</li>
))}
</ul>
</div>
);
}
Define function handler.
import { MouseEvent } from "react";
function ListGroup() {
const items = ["New York", "San Francisco", "Tokyo", "London", "Paris"];
const handleClick = (event: MouseEvent) => {
console.log(event);
};
return (
<div>
<h1>List</h1>
{items.length === 0 && <p>No item found</p>}
<ul className="list-group">
{items.map((item) => (
<li
className="list-group-item"
key={item}
onClick={handleClick}
>
{item}
</li>
))}
</ul>
</div>
);
}
import { MouseEvent } from "react";
function ListGroup() {
const items = ["New York", "San Francisco", "Tokyo", "London", "Paris"];
const handleClick = (item: string, index: number, event: MouseEvent) => {
console.log(item, index, event);
};
return (
<div>
<h1>List</h1>
{items.length === 0 && <p>No item found</p>}
<ul className="list-group">
{items.map((item, index) => (
<li
className="list-group-item"
key={item}
onClick={(event) => handleClick(item, index, event)}
>
{item}
</li>
))}
</ul>
</div>
);
}
Managing State
import { useState } from "react";
function ListGroup() {
const items = ["New York", "San Francisco", "Tokyo", "London", "Paris"];
const [selectedIndex, setSelectedIndex] = useState(-1);
return (
<div>
<h1>List</h1>
{items.length === 0 && <p>No item found</p>}
<ul className="list-group">
{items.map((item, index) => (
<li
className={
selectedIndex === index
? "list-group-item active"
: "list-group-item"
}
key={item}
onClick={() => {setSelectedIndex(index)}}
>
{item}
</li>
))}
</ul>
</div>
);
}
export default ListGroup;
Passing Data via Props
Using interface
import { useState } from "react";
interface Props {
items: string[];
heading: string;
}
function ListGroup({ items, heading }: Props) {
const [selectedIndex, setSelectedIndex] = useState(-1);
return (
<div>
<h1>{heading}</h1>
{items.length === 0 && <p>No item found</p>}
<ul className="list-group">
{items.map((item, index) => (
<li
className={
selectedIndex === index
? "list-group-item active"
: "list-group-item"
}
key={item}
onClick={() => {setSelectedIndex(index)}}
>
{item}
</li>
))}
</ul>
</div>
);
}
export default ListGroup;
import ListGroup from './components/ListGroup';
function App() {
const items = ["New York", "San Francisco", "Tokyo", "London", "Paris"];
return (
<div>
<ListGroup items={items} heading="Cities" />
</div>
);
}
export default App;
Passing Functions via Props
import { useState } from "react";
interface Props {
items: string[];
heading: string;
onSelectItem: (item: string) => void;
}
function ListGroup({ items, heading, onSelectItem }: Props) {
const [selectedIndex, setSelectedIndex] = useState(-1);
return (
<div>
<h1>{heading}</h1>
{items.length === 0 && <p>No item found</p>}
<ul className="list-group">
{items.map((item, index) => (
<li
className={
selectedIndex === index
? "list-group-item active"
: "list-group-item"
}
key={item}
onClick={() => {
setSelectedIndex(index);
onSelectItem(item);
}}
>
{item}
</li>
))}
</ul>
</div>
);
}
export default ListGroup;
import ListGroup from './components/ListGroup';
function App() {
const items = ["New York", "San Francisco", "Tokyo", "London", "Paris"];
const handleSelectItem = (item: string) => {
console.log(item);
};
return (
<div>
<ListGroup items={items} heading="Cities" onSelectItem={handleSelectItem} />
</div>
);
}
export default App;
State vs Props
Props
- Input passed to a component.
- Similar to function args.
- Immutable: can not change inside function, this is call functional programming principles.
State:
- Data managed by a component.
- Similar to local variables.
- Mutable
When they changed, Both will re-render component and update the Dom accordingly.
ES7 React
Cursor install plugin ES7 React
. And input rafce
.
reactArrowFunctionExportComponent
Passing Children
All components support children
prop. children
prop can be any type, such as:
string
ReactNode
for html content.
interface Props {
children: React.ReactNode;
}
function Alert({ children }: Props) {
return <div className="alert alert-primary">{children}</div>;
}
export default Alert;
import Alert from './components/Alert';
function App() {
return (
<div>
<Alert>
Hello World <span>YEAH</span>
</Alert>
</div>
);
}
export default App;