新建React项目
npx create-react-app my-react-app (项目名) (不再是npm 而是npx)
创建成功后cd 项目名进入项目目录
npm start 启动项目
React项目结构
public内是静态页面资源 src内是源码,其中包括初学者最关注的index.js和App.js
index.js(项目入口文件)
文件内引入关键词库 React 和 ReactDOM 通过ReactDOM的createRoot方法创建ReactDOMRoot实例 最后通过Render进行根组件的渲染
App.js(项目根组件)
组件中创建了一个普通函数App,并将其导出(函数组件)
React相关语法及结构
React组件的两种创建方式
- 函数组件(主推)
- 类组件(较复杂/冗rong余)
React语法 JS + HTML = JSX
- 在React中,html结构被放在函数的返回值中,return 后使用圆括号()突破js原语法,表明内部是html结构
- JSX只能返回单个根元素,如果不想加额外容器(div),可以加JSX提供的空标签(<> </>), 相当于vue的template
- React中的每个元素都必须正确的闭合
- React中的属性通常都是驼峰式命名
return (
<>
<img src={logo} className="App-logo" alt="logo" />
</>
);
插值
类似于vue的插值,双括号变为单括号{ },可以同时应用于标签内容和标签属性
条件渲染
function App() {
const flag = false
let divContent
if (flag) {
divContent = <span>Good Morning</span>
} else {
divContent = <p>Good Afternoon</p>
}
return (
<>
<div>{divContent}</div>
</>
);
}
列表渲染
function App() {
const list = [
// 一般不以index作为key,因为index可能会发生变化
{ id: 18, name: '张三' },
{ id: 19, name: '李四' }
]
// 相比于forEach,map有返回值,可以返回一个新数组
const listContent = list.map(item => (
//JSX中只能有一个根元素,所以需要用一个空标签包裹
//如果循环中每次都存在多个根元素,那么就需要使用Fragment
<Fragment key={item.id}>
<li>{item.name}</li>
<p>_________________</p>
</Fragment>
))
return (
<ul>{listContent}</ul>
);
}
事件
function App() {
function handleClick(){
console.log("Button Clicked");
}
return (
<button onClick={handleClick}></button>
);
}
Hooks : useState状态处理
函数式组件默认没有状态,其中的数据只是普通变量,不能实时响应。 为此React提供了一个函数useState,需要传入数据的初始值为参数,函数调用后会返回一个数组[content,setContent],第一个值是对当前数据的引用(本次渲染的数据内容),第二个参数是用来修改状态的函数(一个读一个写)。通过函数来调用并传入新内容。
useState处理变量
function App() {
//解构useState返回的数组
const [content, setDivContent] = useState('Hello World')
function handleClick() {
setDivContent('Hello React')
}
return (
<>
<div>{content}</div>
<button onClick={handleClick}></button>
</>
);
}
useState处理对象
function App() {
const [data, setData] = useState({
content: 'Hello React',
title: 'Hello World',
})
function handleClick() {
//setData会把data里的所有属性都替换掉
//所以要保留原来的属性,需要使用扩展运算符
setData({
...data,
title: 'Hello',
})
}
return (
<>
<div title={data.title}>{data.content}</div>
<button onClick={handleClick}></button>
</>
);
}
useState处理数组
function App() {
const [data, setData] = useState([
{ id: 18, name: '张三' },
{ id: 19, name: '李四' }
])
const listContent = data.map(item => (
<li key={item.id}>{item.name}</li>
))
function handleClick() {
setData(data.filter(item => item.id!== 2))
}
return (
<>
<ul>{listContent}</ul>
<button onClick={handleClick}></button>
</>
);
}
React Hooks 实现同步useState
React的useState在set某个值之后虽然视图回刷新,但是无法直接在js中获取到最新的值(更新是异步的),无法实现类似于赋值后回调的操作。但是useRef的特性正好跟useState相反,是可以直接在js中同步更新但是视图不会刷新
简单来说就是组合useState和useRef,制造一个新的hooks来替代useState,在jsx这种照样像useState一样使用,但是在需要做同步代码(回调)的时候使用ref去读取,保证能获取到最新的值。
import { MutableRefObject, useRef, useState } from "react";
/**
* 定义一个同步状态管理的钩子,用于在React组件中同步管理状态
* 它提供了初始状态设置、状态更新和状态引用的功能
*/
export type UseSyncState = <T = any>(
initialValue: T,
) => [T, (input: T) => void, MutableRefObject<T>];
/**
* 实现useSyncState钩子
* @param initialValue 初始状态值
* @returns 返回一个包含当前状态、更新状态函数和状态引用的数组
*/
export const useSyncState: UseSyncState = <T = any,>(initialValue: T) => {
// 使用useState钩子管理组件的状态
const [state, setState] = useState(initialValue);
// 使用useRef钩子创建一个状态的ref,用于在组件更新时保持对当前状态的引用
const stateRef = useRef<T>(state);
/**
* 同步更新状态的函数,支持传入新的状态值或一个函数,该函数接收上一个状态并返回新的状态
* @param v
*/
const syncSetState: (input: T) => void = (v) => {
setState(v);
stateRef.current = v;
};
// 返回当前状态、更新状态的函数和状态的ref
return [state, syncSetState, stateRef];
};
使用
import { useSyncState } from "@/utils/hooks/useSyncState";
import { Button, Space } from "antd";
export const Test: React.FC = () => {
const [count, setCount, countRef] = useSyncState(0);
const handleAddCount = () => {
setCount(countRef.current + 1); //直接能获取到最新的值进行计算
console.log(countRef.current); // 直接能打印出最新的值
};
return (
<div>
<Space>
{/* 视图会显示出最新的值 */}
{count}
<Button onClick={handleAddCount}></Button>
</Space>
</div>
);
};
export default Test;
因为永远可以获取到最新的值,所以setState函数不再需要通过函数的方式传入。