Skip to content

React 基础

配套资料

阿里云盘分享

React 起源与发展

React 与传统 MVC 的关系

React 的特性

虚拟 DOM

为什么学习 React?

react 文档

配置开发环境

Terminal window
npm install -g create-react-app
Terminal window
create-react-app 项目名称
Terminal window
npx create-react-app 项目名称

npm 和 npx 的区别

页面的显示流程

Terminal window
create-react-app react-demo
Terminal window
npm run start

端口以自己的为准, 默认为 3000

NPM vs YARN

npm i
yarn
npm i {库名}
yarn add {库名}
npm i {库名} --save-dev
yarn add {库名} --dev
npm uninstall package --save
yarn remove package
npm update --save
yarn upgrade
npm install package -g
yarn global add package

编写第一个 react 应用程序

import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<h1>欢迎进入React的世界</h1>);

什么是 jsx

注意:JSX 并不是标准的 JS 语法,是 JS 的语法扩展,浏览器默认是不识别的,脚手架中内置的 @babel/plugin-transform-react-jsx 包,用来解析该语法 如下图, 左边使我们编写的声明式代码, 右边是解析后的命令式代码

JSX 中使用 js 表达式

import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<h1>欢迎进入React的世界</h1>);
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
// 定义一个用户名
const username = "张三";
root.render(<h1>你好, 我叫{username}</h1>);

特别注意: if 语句/ switch-case 语句/ 变量声明语句,这些叫做语句,不是表达式,不能出现在 {} 中!!

import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
// 定义一个用户名
const username = "张三";
// 定义一个函数, 用来计算年龄
const getAge = () => {
return 30;
};
root.render(
<h1>
你好, 我叫{username} , 今年 {getAge()} 岁了!!!
</h1>
);

import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
// 定义一个用户名
const username = "张三";
// 定义一个函数, 用来计算年龄
const getAge = () => {
return 30;
};
// 定义一个布尔值
const flag = true;
root.render(
<h1>
你好, 我叫{username} , 今年 {getAge()} 岁了!!!
{flag ? "已经结婚了" : "还没结婚"}
</h1>
);

JSX 列表渲染

const songs = [
{ id: 1, name: "粉红高跟鞋" },
{ id: 2, name: "伤心太平洋" },
{ id: 3, name: "雨一直下" },
];
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
// 定义一个歌单
const songs = [
{ id: 1, name: "粉红高跟鞋" },
{ id: 2, name: "伤心太平洋" },
{ id: 3, name: "雨一直下" },
];
root.render(
<div>
<h1>歌单</h1>
<ul>
{songs.map((song) => (
<li key={song.id}>{song.name}</li>
))}
</ul>
</div>
);
<li key={song.id}>{song.name}</li>

  1. key 在 HTML 结构中是看不到的,是 React 内部用来进行性能优化时使用
  2. key 在当前列表中要唯一的字符串或者数值(String/Number)
  3. 如果列表中有像 id 这种的唯一值,就用 id 来作为 key 值
  4. 如果列表中没有像 id 这种的唯一值,就可以使用 index(下标)来作为 key 值

JSX 条件渲染

import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
const flag = true;
root.render(<h1>{flag ? "好棒棒!!!" : "真菜..."}</h1>);

import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
const type = 1;
const getFlag = () => {
if (type === 1) {
return <h1>this is h1</h1>;
}
if (type === 2) {
return <h2>this is h2</h2>;
}
if (type === 3) {
return <h3>this is h3</h3>;
}
};
root.render(<div>{getFlag()}</div>);

JSX 样式处理

import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
const color = "red";
const backgroundColor = "black";
root.render(<h1 style={{ color: color, backgroundColor: backgroundColor }}>this is h1</h1>);

root.render(<h1 style={{ color, backgroundColor }}>this is h1</h1>);
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
const color = "red";
const backgroundColor = "black";
const styleObj = {
color,
backgroundColor,
};
root.render(<h1 style={styleObj}>this is h1</h1>);

.title {
color: olivedrab;
font-size: 44px;
}
import ReactDOM from "react-dom/client";
// 导入css
import "./app.css";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<h1 className="title">this is h1</h1>);

.title {
color: orangered;
font-size: 48px;
}
src\index.js
import ReactDOM from "react-dom/client";
import "./app.css";
const showTitle = true;
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<h1 className={showTitle ? "title" : ""}>hello world</h1>);

JSX 注意事项

src\index.js
import ReactDOM from "react-dom/client";
import "./app.css";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<h1>hello world</h1>
<h1>hello world</h1>
<h1>hello world</h1>
<h1>hello world</h1>
);

import ReactDOM from "react-dom/client";
import "./app.css";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<>
<h1>hello world</h1>
<h1>hello world</h1>
<h1>hello world</h1>
<h1>hello world</h1>
</>
);
import ReactDOM from "react-dom/client";
import "./app.css";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
// class属性换成className
<form action="" className="myForm">
gender:
<input type="radio" name="" id="myGender" />
{/* for属性换成htmlFor */}
<label htmlFor="myGender">male</label>
</form>
);

vscode 插件

🚩jsx 练习

react-jsx-demo.zip

Terminal window
npm i
或者
yarn
Terminal window
npm run start
或者
yarn start

list: [
{
id: 1,
author: "刘德华",
comment: "给我一杯忘情水",
time: 1633828140000,
// 1: 点赞 0:无态度 -1:踩
attitude: 1,
},
{
id: 2,
author: "周杰伦",
comment: "哎哟,不错哦",
time: 1633914540000,
// 1: 点赞 0:无态度 -1:踩
attitude: 0,
},
{
id: 3,
author: "五月天",
comment: "不打扰,是我的温柔",
time: 1633918140000,
// 1: 点赞 0:无态度 -1:踩
attitude: -1,
},
],
{
/* 评论列表 */
}
<div className="comment-list">
{state.list.map((item) => {
return (
<div className="list-item" key={item.id}>
<div className="user-face">
<img className="user-head" src={avatar} alt="" />
</div>
<div className="comment">
<div className="user">尤雨溪</div>
<p className="text">前排吃瓜</p>
<div className="info">
<span className="time">2021-10-08 09:05:00</span>
<span className="like liked">
<i className="icon" />
</span>
<span className="hate hated">
<i className="icon" />
</span>
<span className="reply btn-hover">删除</span>
</div>
</div>
</div>
);
})}
</div>;

/**
* 时间戳转日期
* @param {number} timestamp 毫秒级时间戳
* @returns {string} "2022-12-9 19:00:34"
*/
function timestampToTime(timestamp) {
var date = new Date(timestamp);
var Y = date.getFullYear() + "-";
var M = (date.getMonth() + 1 < 10 ? "0" + (date.getMonth() + 1) : date.getMonth() + 1) + "-";
var D = date.getDate() + " ";
var h = (date.getHours() + "").padStart(2, "0") + ":"; // 两位, 前补零
var m = (date.getMinutes() + "").padStart(2, "0") + ":"; // 两位, 前补零
var s = (date.getSeconds() + "").padStart(2, "0"); // 两位, 前补零
return Y + M + D + h + m + s;
}

<div className="info">
<span className="time">{timestampToTime(item.time)}</span>
<span className={item.attitude === 1 ? "like liked" : "like"}>
<i className="icon" />
</span>
<span className={item.attitude === -1 ? "hate hated" : "hate"}>
<i className="icon" />
</span>
<span className="reply btn-hover">删除</span>
</div>

{
/* 排序 */
}
<div className="tabs-order">
<ul className="sort-container">
{state.tabs.map((item) => {
return <li className="on">按热度排序</li>;
})}
</ul>
</div>;

{
/* 排序 */
}
<div className="tabs-order">
<ul className="sort-container">
{state.tabs.map((item) => {
return (
<li key={item.id} className="on">
{item.name}排序
</li>
);
})}
</ul>
</div>;

<li key={item.id} className={item.type === state.active ? "on" : ""}>
{item.name}排序
</li>

react 组件

函数组件

Terminal window
npx create-react-app react-basic
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<div>hello world</div>);
npm start

import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
// 函数组件
function HelloFunctionComponent() {
return <h1>hello world</h1>;
}
root.render(
<div>
{/* 单标签 */}
<HelloFunctionComponent />
{/* 双标签 */}
<HelloFunctionComponent></HelloFunctionComponent>
</div>
);

  1. 组件的名称必须首字母大写(大驼峰的写法),react 内部会根据这个来判断是组件还是普通的 HTML 标签
  2. 函数组件必须有返回值,表示该组件的 UI 结构;
  3. 如果不需要渲染任何内容,则返回 null
  4. 组件就像 HTML 标签一样可以被渲染到页面中。
  5. 组件表示的是一段结构内容,对于函数组件来说,渲染的内容是函数的返回值
  6. 使用函数名称作为组件标签名称,可以是双标签也可以是单标签

类组件

src\index.js
// 引入React
import React from "react";
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
// 定义类组件
class HelloClassComponent extends React.Component {
render() {
return <div>这是我的第一个类组件!</div>;
}
}
root.render(
<div>
{/* 渲染类组件 */}
{/* 单标签 */}
<HelloClassComponent />
{/* 双标签 */}
<HelloClassComponent></HelloClassComponent>
</div>
);

  1. 类名称也必须以大写字母开头, 大驼峰写法
  2. 类组件应该继承 React.Component 父类,从而使用父类中提供的方法或属性
  3. 类组件必须提供 render 方法
  4. render 方法必须有返回值,表示该组件的 UI 结构

事件绑定

<div onClick={() => {}}></div>
src\index.js
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
// 函数组件
function HelloFun() {
// 定义事件回调函数
const clickHandler = () => {
alert("事件被触发了");
};
return (
// 绑定事件
<button onClick={clickHandler}>click me!</button>
);
}
root.render(
<div>
{/* 单标签 */}
<HelloFun />
{/* 多标签 */}
<HelloFun></HelloFun>
</div>
);
src\index.js
// 引入React
import React from "react";
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
// 定义类组件
class HelloClassComponent extends React.Component {
// 定义事件回调函数
clickHandler = () => {
alert("事件被触发了");
};
render() {
return (
// 绑定事件
<button onClick={this.clickHandler}>类组件 click me!</button>
);
}
}
root.render(
<div>
{/* 渲染类组件 */}
{/* 单标签 */}
<HelloClassComponent />
{/* 双标签 */}
<HelloClassComponent></HelloClassComponent>
</div>
);
// 定义事件回调函数
clickHandler() {
alert("事件被触发了");
}
clickHandler() {
console.log(this); // undefined
}
clickHandler = () => {
console.log(this); // HelloClassComponent
};

获取事件对象

src\index.js
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
// 函数组件
function HelloFun() {
// 定义事件处理函数
const clickHandler = (e) => {
// 阻止默认事件, 不让a标签跳转
e.preventDefault();
alert("点击事件已经被触发了...");
};
// 绑定点击事件
return (
<a onClick={clickHandler} href="https://www.baidu.com">
baidu
</a>
);
}
root.render(
<div>
{/* 单标签 */}
<HelloFun />
</div>
);

传递额外参数

src\index.js
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
// 函数组件
function HelloFun() {
// 定义事件回调函数
const clickHandler = () => {
alert("事件被触发了");
};
return (
// 绑定事件
<button onClick={clickHandler}>click me!</button>
);
}
root.render(
<div>
{/* 单标签 */}
<HelloFun />
</div>
);
src\index.js
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
// 函数组件
function HelloFun() {
// 定义事件回调函数
const clickHandler = (a, b) => {
alert("事件被触发了");
alert(a);
alert(b);
};
return (
// 绑定事件
<button
onClick={() => {
clickHandler("hello", "world");
}}
>
click me!
</button>
);
}
root.render(
<div>
{/* 单标签 */}
<HelloFun />
</div>
);
src\index.js
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
// 函数组件
function HelloFun() {
// 定义事件回调函数
const clickHandler = (e, a, b) => {
console.log(e);
alert("事件被触发了");
alert(a);
alert(b);
};
return (
// 绑定事件
<button
onClick={(e) => {
clickHandler(e, "hello", "world");
}}
>
click me!
</button>
);
}
root.render(
<div>
{/* 单标签 */}
<HelloFun />
</div>
);

类组件的事件绑定

src\index.js
// 引入React
import React from "react";
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
// 定义类组件
class HelloClassComponent extends React.Component {
// 定义事件回调函数
clickHandler = () => {
alert("事件被触发了");
};
render() {
return (
// 绑定事件
<button onClick={this.clickHandler}>类组件 click me!</button>
);
}
}
root.render(
<div>
{/* 单标签 */}
<HelloClassComponent />
</div>
);
src\index.js
// 引入React
import React from "react";
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
// 定义类组件
class HelloClassComponent extends React.Component {
// 定义事件回调函数
clickHandler = (a, b) => {
alert("事件被触发了");
alert(a);
alert(b);
};
render() {
return (
// 绑定事件
<button
onClick={() => {
this.clickHandler("hello", "world");
}}
>
类组件 click me!
</button>
);
}
}
root.render(
<div>
{/* 单标签 */}
<HelloClassComponent />
</div>
);
src\index.js
// 引入React
import React from "react";
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
// 定义类组件
class HelloClassComponent extends React.Component {
// 定义事件回调函数
clickHandler = (e, a, b) => {
console.log(e);
console.log(a, b);
};
render() {
return (
// 绑定事件
<button
onClick={(e) => {
this.clickHandler(e, "hello", "world");
}}
>
类组件 click me!
</button>
);
}
}
root.render(
<div>
{/* 单标签 */}
<HelloClassComponent />
</div>
);

组件状态

初始化状态

state = {
count: 0,
};
src\index.js
// 引入React
import React from "react";
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
// 定义类组件
class Counter extends React.Component {
// 定义状态
state = {
count: 0,
};
render() {
return <button>计数器</button>;
}
}
root.render(
<div>
<Counter></Counter>
</div>
);

读取状态

src\index.js
// 引入React
import React from "react";
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
// 定义类组件
class Counter extends React.Component {
// 定义状态
state = {
count: 0,
};
render() {
return (
<>
<button>计数器</button>
<p>当前计数: {this.state.count}</p>
</>
);
}
}
root.render(
<div>
<Counter></Counter>
</div>
);

修改状态

this.setState({ 要修改的部分数据 });
src\index.js
// 引入React
import React from "react";
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
// 定义类组件
class Counter extends React.Component {
// 定义状态
state = {
count: 0,
};
changeCount = () => {
this.setState({
count: this.state.count + 1,
});
};
render() {
return (
<>
<button onClick={this.changeCount}>计数器</button>
<p>当前计数: {this.state.count}</p>
</>
);
}
}
root.render(
<div>
<Counter></Counter>
</div>
);

React 的状态不可变

src\index.js
// 引入React
import React from "react";
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
// 定义类组件
class Counter extends React.Component {
// 定义状态
state = {
count: 0,
};
changeCount = () => {
this.setState({
count: this.state.count + 1,
});
};
render() {
return (
<>
<button onClick={this.changeCount}>计数器</button>
<p>当前计数: {this.state.count}</p>
</>
);
}
}
root.render(
<div>
<Counter></Counter>
</div>
);
changeCount = () => {
this.setState({
count: this.state.count++,
});
};

state = {
count: 0,
list: [1, 2, 3],
person: {
name: "jack",
age: 18,
},
};
// 直接修改简单类型Number
this.state.count++;
++this.state.count;
this.state.count += 1;
this.state.count = 1;
// 直接修改数组
this.state.list.push(123);
this.state.list.slice(1, 1);
// 直接修改对象
this.state.person.name = "rose";
this.setState({
count: this.state.count + 1
list: [...this.state.list, 4],
person: {
...this.state.person,
// 覆盖原来的属性 就可以达到修改对象中属性的目的
name: 'rose'
}
})
// 类组件
class HelloClassComponent extends React.Component {
state = {
list: [1, 2, 3],
};
changeState = () => {
this.setState({
list: this.state.list.push(4), // ×
// list: [...this.state.list, 4], // √
});
};
render() {
return (
<>
<button onClick={this.changeState}>click me</button>
<p>当前state: {JSON.stringify(this.state.list)}</p>
</>
);
}
}
// 类组件
class HelloClassComponent extends React.Component {
state = {
person: {
name: "jack",
age: 18,
},
};
changeState = () => {
// 正确写法
this.setState({
person: { ...this.state.person, age: 19 },
});
};
render() {
return (
<>
<button onClick={this.changeState}>click me</button>
<p>当前state: {JSON.stringify(this.state.person)}</p>
</>
);
}
}

表单处理

受控表单组件

src\index.js
// 引入React
import React from "react";
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
// 定义类组件
class InputComponent extends React.Component {
// 1. 在组件的state中声明一个组件的状态数据,
state = {
message: "this is message",
};
render() {
// 2. 将input标签元素的value属性的值设置为状态数据
return <input type="text" value={this.state.message} />;
}
}
root.render(
<div>
<InputComponent />
</div>
);

// 定义类组件
class InputComponent extends React.Component {
// 1. 在组件的state中声明一个组件的状态数据,
state = {
message: "this is message",
};
// 3.2 定义inputChange方法
inputChange = () => {
console.log("input value is change...");
};
render() {
// 2. 将input标签元素的value属性的值设置为状态数据
// 3.1 为input添加change事件
return <input type="text" onChange={this.inputChange} value={this.state.message} />;
}
}

inputChange = (e) => {
// 4. 在事件处理程序中,通过事件对象e获取到当前文本框的值(`即用户当前输入的值`)
const value = e.target.value;
console.log(value);
};
inputChange = (e) => {
// 4. 在事件处理程序中,通过事件对象e获取到当前文本框的值(`即用户当前输入的值`)
const value = e.target.value;
// 5. 调用setState方法,将文本框的值作为state状态的最新值
this.setState({
message: value,
});
};

非受控表单组件

// 1. 导入createRef 函数
import React, { createRef } from "react";
class InputComponent extends React.Component {
// 2. 调用createRef函数,创建一个ref对象,存储到名为msgRef的实例属性中
msgRef = createRef();
}
// 定义类组件
class InputComponent extends React.Component {
// 2. 调用createRef函数,创建一个ref对象,存储到名为msgRef的实例属性中
msgRef = createRef();
render() {
return (
<>
{/* 3. 为input添加ref属性,值为msgRef */}
<input ref={this.msgRef} />
</>
);
}
}
src\index.js
// 引入React
// 1. 导入createRef 函数
import { render } from "@testing-library/react";
import React, { createRef } from "react";
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
// 定义类组件
class InputComponent extends React.Component {
// 2. 调用createRef函数,创建一个ref对象,存储到名为msgRef的实例属性中
msgRef = createRef();
// 4.2. 在按钮的事件处理程序中,通过msgRef.current即可拿到input对应的dom元素,而其中msgRef.current.value拿到的就是文本框的值
changeHandler = () => {
console.log(this.msgRef.current.value);
};
render() {
return (
<>
{/* 3. 为input添加ref属性,值为msgRef */}
<input ref={this.msgRef} />
{/* 4.1 添加点击按钮 */}
<button onClick={this.changeHandler}>click</button>
</>
);
}
}
root.render(
<div>
<InputComponent />
</div>
);

🚩 组件基础小练习

react-jsx-demo.zip

Terminal window
npm i
或者
yarn

Terminal window
npm run start
或者
npm start

switchTab = (type) => {
// 点击之后, 把type给state的active
this.setState({
active: type,
});
};

textareaChange = (e) => {
this.setState({
comment: e.target.value,
});
};

// 相关的方法, 提交评论
submitComment = () => {
// 1. 获取评论内容
const comment = this.state.comment;
// 2. 判断是否为空
if (comment.trim() === "") {
alert("评论内容不能为空");
return;
}
// 3. 构造评论对象
const obj = {
id: Date.now(),
author: "匿名用户",
comment: comment,
time: Date.now(),
attitude: 0,
};
// 4. 更新状态
this.setState({
list: [obj, ...this.state.list],
comment: "",
});
};
Terminal window
npm i uuid
或者
yarn add uuid
import { v4 as uuid } from "uuid";

a3dd49f0-b463-477f-8bd9-88284fd0242b

// 相关的方法, 删除评论
deleteComment = (id) => {
// 1. 获取原来的list
const list = this.state.list;
// 2. 过滤
const newList = list.filter((item) => {
return item.id !== id;
});
// 3. 更新状态
this.setState({
list: newList,
});
};

// 相关的方法, 点赞
switchLike = (id) => {
// 1. 获取原来的list
const list = this.state.list;
// 2. 遍历
const newList = list.map((item) => {
if (item.id === id) {
if (item.attitude === 1) {
item.attitude = 0;
} else {
item.attitude = 1;
}
}
return item;
});
// 3. 更新状态
this.setState({
list: newList,
});
};
// 相关的方法, 踩
switchHate = (id) => {
// 1. 获取原来的list
const list = this.state.list;
// 2. 遍历
const newList = list.map((item) => {
if (item.id === id) {
if (item.attitude === -1) {
item.attitude = 0;
} else {
item.attitude = -1;
}
}
return item;
});
// 3. 更新状态
this.setState({
list: newList,
});
};

react-jsx-2.zip

React 组件通信

父传子

父传子 类组件

src\index.js
import React from "react";
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
// 类子组件
class SonClass extends React.Component {
render() {
return <div>我是类子组件</div>;
}
}
// ParentClass 父组件 Son 子组件
class ParentClass extends React.Component {
render() {
return (
<div>
<SonClass />
</div>
);
}
}
root.render(
<>
<ParentClass />
</>
);

// ParentClass 父组件 Son 子组件
class ParentClass extends React.Component {
state = {
message: "this is message from parent",
};
render() {
return (
<div>
<SonClass />
</div>
);
}
}
// 类子组件
class SonClass extends React.Component {
render() {
return <div>我是类子组件, {this.props.msg}</div>;
}
}

父传子 函数式组件

src\index.js
import React from "react";
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
// 函数式子组件
function SonFun() {
return <div>我是函数子组件</div>;
}
// ParentFun 父组件 Son 子组件
class ParentFun extends React.Component {
render() {
return (
<div>
<SonFun />
</div>
);
}
}
root.render(
<>
<ParentFun />
</>
);

// ParentFun 父组件 Son 子组件
class ParentFun extends React.Component {
state = {
message: "this is message from parent",
};
render() {
return (
<div>
<SonFun />
</div>
);
}
}

// 函数式子组件
function SonFun(props) {
return <div>我是函数子组件, {props.msg}</div>;
}

props 说明

src\index.js
import React from "react";
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
// 函数式子组件
function SonFun(props) {
return (
<div>
我是函数子组件
<ul>
{props.list.map((item, index) => {
return <li key={index}>{item}</li>;
})}
</ul>
</div>
);
}
// 类子组件
class SonCls extends React.Component {
render() {
return (
<div>
我是类子组件
<ul>
{this.props.list.map((item, index) => {
return <li key={index}>{item}</li>;
})}
</ul>
</div>
);
}
}
// Parent 父组件 Son 子组件
class Parent extends React.Component {
// 准备数据
state = {
list: [1, 2, 3, 4],
};
render() {
return (
<div>
<SonFun list={this.state.list} />
<SonCls list={this.state.list} />
</div>
);
}
}
root.render(
<>
<Parent />
</>
);

src\index.js
import React from "react";
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
// 函数式子组件
function SonFun(props) {
return <div>我是函数子组件 ,我叫{props.userinfo.username}</div>;
}
// 类子组件
class SonCls extends React.Component {
render() {
return <div>我是类子组件,我叫{this.props.userinfo.username}</div>;
}
}
// Parent 父组件 Son 子组件
class Parent extends React.Component {
// 准备数据
state = {
userinfo: {
username: "lisi",
age: 18,
},
};
render() {
return (
<div>
<SonFun userinfo={this.state.userinfo} />
<SonCls userinfo={this.state.userinfo} />
</div>
);
}
}
root.render(
<>
<Parent />
</>
);

src\index.js
import React from "react";
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
// 函数式子组件
function SonFun(props) {
return (
<>
<div>这是子组件SonFun</div>
<button onClick={props.getMsg}>点击</button>
</>
);
}
// 类子组件
class SonCls extends React.Component {
render() {
return (
<>
<div>这是子组件SonFun</div>
<button onClick={this.props.getMsg}>点击</button>
</>
);
}
}
// Parent 父组件 Son 子组件
class Parent extends React.Component {
getMsg = () => {
console.log("父组件函数");
};
render() {
return (
<div>
<SonFun getMsg={this.getMsg} />
<SonCls getMsg={this.getMsg} />
</div>
);
}
}
root.render(
<>
<Parent />
</>
);

src\index.js
import React from "react";
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
// 函数式子组件
function SonFun(props) {
return (
<>
<div>这是子组件SonFun</div>
{props.child}
</>
);
}
// 类子组件
class SonCls extends React.Component {
render() {
return (
<>
<div>这是子组件SonFun</div>
{this.props.child}
</>
);
}
}
// Parent 父组件 Son 子组件
class Parent extends React.Component {
render() {
return (
<div>
<SonFun child={<div>this is div</div>} />
<SonCls child={<div>this is div</div>} />
</div>
);
}
}
root.render(
<>
<Parent />
</>
);

子传父

import React from "react";
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
// 函数子组件
function SonFun() {
return <div>this is son</div>;
}
// 父组件
class Parent extends React.Component {
render() {
return (
<>
<SonFun />
</>
);
}
}
root.render(
<>
<Parent />
</>
);

// 父组件
class Parent extends React.Component {
// 1. 父组件提供一个回调函数 - 用于接收数据
getSonMsg = (sonMsg) => {
console.log(sonMsg);
};
render() {
return (
<>
{/* 2. 将函数作为属性的值,传给子组件 */}
<SonFun getMsg={this.getSonMsg} />
</>
);
}
}
// 函数子组件
function SonFun(props) {
// 3. 子组件通过 props 调用回调函数
const { getMsg } = props;
return <div>this is son</div>;
}
// 函数子组件
function SonFun(props) {
// 3. 子组件通过 props 调用回调函数
const { getMsg } = props;
// 4. 将子组件中的数据作为参数传递给回调函数
return (
<>
<div>this is son </div>
<button onClick={() => getMsg("这是子组件的数据")}>点击传递数据给父组件</button>
</>
);
}

兄弟组件通信

import React from "react";
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
// 函数式子组件
function SonA(props) {
return <div>this is SonA</div>;
}
function SonB(props) {
return <div>this is SonB</div>;
}
// Parent 父组件 Son 子组件
class Parent extends React.Component {
render() {
return (
<div>
<SonA />
<SonB />
</div>
);
}
}
root.render(
<>
<Parent />
</>
);

// Parent 父组件 Son 子组件
class Parent extends React.Component {
getBMsg = (msg) => {
console.log(msg);
};
render() {
return (
<div>
<SonA />
<SonB getBMsg={this.getBMsg} />
</div>
);
}
}
function SonB(props) {
const { getBMsg } = props;
return (
<div>
this is SonB
<button onClick={() => getBMsg("这是B组件的数据")}>点击传值</button>
</div>
);
}

// Parent 父组件 Son 子组件
class Parent extends React.Component {
getBMsg = (msg) => {
console.log(msg);
};
state = {
sendAMsg: "测试一下父传子, 初始值...",
};
render() {
return (
<div>
<SonA sendAMsg={this.state.sendAMsg} />
<SonB getBMsg={this.getBMsg} />
</div>
);
}
}
// 函数式子组件
function SonA(props) {
return <div>this is SonA {props.sendAMsg}</div>;
}

getBMsg = (msg) => {
this.setState({
sendAMsg: msg,
});
};

跨组件通信 Context

const { Provider, Consumer } = createContext();
<Provider value={this.state.message}>{/* 根组件 */}</Provider>
<Consumer >
{value => /* 基于 context 值进行渲染*/}
</Consumer>
import React from "react";
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
function ComponentC() {
return <div>this is ComponentC</div>;
}
function ComponentA() {
return (
<div>
this is ComponentA
<ComponentC />
</div>
);
}
class Parent extends React.Component {
render() {
return (
<div>
this is Parent
<ComponentA />
</div>
);
}
}
root.render(
<>
<Parent />
</>
);

// 2. 如果没有导入, 记得导入createContext
import React, { createContext } from "react";
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
// 1. 获取Provider, Consumer
const { Provider, Consumer } = createContext();

class Parent extends React.Component {
// 3. 定义需要传递的数据
state = {
message: "这是根组件Parent要传递的数据...",
};
render() {
return (
<Provider value={this.state.message}>
<div>
this is Parent
<ComponentA />
</div>
</Provider>
);
}
}
function ComponentC() {
return (
<div>
this is ComponentC,
<Consumer>
{/* 使用函数获取数据, 参数value是传过来的数据 */}
{(value) => <b>{value}</b>}
</Consumer>
</div>
);
}

🚩 组件小练习

[
{ id: 1, name: "超级好吃的棒棒糖", price: 18.8, info: "开业大酬宾,全场8折" },
{ id: 2, name: "超级好吃的大鸡腿", price: 34.2, info: "开业大酬宾,全场8折" },
{ id: 3, name: "超级无敌的冰激凌", price: 14.2, info: "开业大酬宾,全场8折" },
];

import React from "react";
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
// 子组件
function ListItem(props) {
return <div>this is ListItem</div>;
}
// 父组件
class App extends React.Component {
render() {
return (
<>
<ListItem />
</>
);
}
}
root.render(
<>
<App />
</>
);

// 父组件
class App extends React.Component {
state = {
list: [
{ id: 1, name: "超级好吃的棒棒糖", price: 18.8, info: "开业大酬宾,全场8折" },
{ id: 2, name: "超级好吃的大鸡腿", price: 34.2, info: "开业大酬宾,全场8折" },
{ id: 3, name: "超级无敌的冰激凌", price: 14.2, info: "开业大酬宾,全场8折" },
],
};
render() {
return (
<>
<ListItem />
</>
);
}
}

render() {
return (
<>
{this.state.list.map((item) => (
<ListItem key={item.id} {...item} />
))}
</>
);
}
function ListItem(props) {
const { name, price, info } = props;
return (
<div>
<h3>{name}</h3>
<p>{price}</p>
<p>{info}</p>
</div>
);
}

delHandler = (id) => {
this.setState({
list: this.state.list.filter((item) => item.id !== id),
});
};

<ListItem key={item.id} {...item} delHandler={this.delHandler} />

<button onClick={() => delHandler(id)}>删除</button>

react-demo.zip

children 属性

import React from "react";
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
function ComponentA(props) {
console.log(props.children);
return <div>this is A</div>;
}
class Parent extends React.Component {
render() {
return (
<ComponentA>
<div>hello world</div>
</ComponentA>
);
}
}
root.render(
<>
<Parent />
</>
);
function ComponentA(props) {
const { children } = props;
return <div>this is A {children}</div>;
}
class Parent extends React.Component {
render() {
return <ComponentA>这是一段文本</ComponentA>;
}
}

function ComponentA(props) {
const { children } = props;
return <div>this is A {children}</div>;
}
class Parent extends React.Component {
render() {
return (
<ComponentA>
<b>这是一个加粗标签</b>
</ComponentA>
);
}
}

function ComponentA(props) {
const { children } = props;
return <div>this is A{children.map((item) => item)}</div>;
}
class Parent extends React.Component {
render() {
return (
<ComponentA>
<b>这是一个加粗标签1</b>
<b>这是一个加粗标签2</b>
<b>这是一个加粗标签3</b>
</ComponentA>
);
}
}
function ComponentA(props) {
const { children } = props;
return <div>this is A {children()}</div>;
}
class Parent extends React.Component {
render() {
return <ComponentA>{() => "我是一个函数..."}</ComponentA>;
}
}

import React from "react";
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
function ComponentA(props) {
const { children } = props;
return <div>this is A {children}</div>;
}
class Parent extends React.Component {
render() {
return (
<ComponentA>
{
<div>
<b>{"this is jsx"}</b>
</div>
}
</ComponentA>
);
}
}
root.render(
<>
<Parent />
</>
);

props 校验-场景和使用

import React from "react";
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
function Son() {
return (
<ul>
<li>子组件</li>
</ul>
);
}
class Parent extends React.Component {
render() {
return (
<div>
<Son />
</div>
);
}
}
root.render(
<>
<Parent />
</>
);

class Parent extends React.Component {
render() {
return (
<div>
<Son colors={["red", "blue", "yellow"]} />
</div>
);
}
}
function Son(props) {
const colorArr = props.colors;
const list = colorArr.map((item, index) => {
return <li key={index}>{item}</li>;
});
return <ul>{list}</ul>;
}

Terminal window
npm install prop-types
或者
yarn add prop-types
import React from "react";
import ReactDOM from "react-dom/client";
// 导入 prop-types包
import PropTypes from "prop-types";
const root = ReactDOM.createRoot(document.getElementById("root"));
Son.propTypes = {
colors: PropTypes.array,
};

return (
<div>
<Son colors={18} />
</div>
);

props 校验-规则说明

// 常见类型
optionalFunc: PropTypes.func,
// 必填 只需要在类型后面串联一个isRequired
requiredFunc: PropTypes.func.isRequired,
// 特定结构的对象
optionalObjectWithShape: PropTypes.shape({
color: PropTypes.string,
fontSize: PropTypes.number
})

使用 PropTypes 进行类型检查 – React

import React from "react";
import ReactDOM from "react-dom/client";
// 导入 prop-types包
import PropTypes from "prop-types";
const root = ReactDOM.createRoot(document.getElementById("root"));
function Son(props) {
const colorArr = props.colors;
const list = colorArr.map((item, index) => {
return <li key={index}>{item}</li>;
});
return <ul>{list}</ul>;
}
Son.propTypes = {
// 必须是数组, 而且是必传
colors: PropTypes.array.isRequired,
};
class Parent extends React.Component {
render() {
return (
<div>
{/* 没有传值 */}
<Son />
</div>
);
}
}
root.render(
<>
<Parent />
</>
);

props 校验-默认值

props 校验-默认值-函数组件

import React from "react";
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
function Son() {
return <h1>函数子组件</h1>;
}
class Parent extends React.Component {
render() {
return (
<div>
<Son />
</div>
);
}
}
root.render(
<>
<Parent />
</>
);

Terminal window
npm install prop-types
或者
yarn add prop-types
import PropTypes from "prop-types";

// 2. 定义类型校验
Son.propTypes = {
colors: PropTypes.array,
};

// 3. 定义默认值, 如果父组件不进行传值, 走默认
Son.defaultProps = {
colors: ["aaa", "bbb", "ccc", "ddd"],
};

return (
<div>
<Son colors={["red", "yellow", "blue"]} />
</div>
);

function Son(props) {
const colorArr = props.colors;
const list = colorArr.map((item, index) => {
return <li key={index}>{item}</li>;
});
return <ul>{list}</ul>;
}

// 5. 子组件接收值
function Son({ colors = ["aaa", "bbb", "ccc", "ddd", "eee"] }) {
const colorArr = colors;
const list = colorArr.map((item, index) => {
return <li key={index}>{item}</li>;
});
return <ul>{list}</ul>;
}

props 校验-默认值-类组件

import React from "react";
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
class Son extends React.Component {
render() {
return <h1>这是子组件</h1>;
}
}
class Parent extends React.Component {
render() {
return (
<div>
<Son />
</div>
);
}
}
root.render(
<>
<Parent />
</>
);

Terminal window
npm install prop-types
或者
yarn add prop-types
import PropTypes from "prop-types";

// 2. 给默认值
Son.defaultProps = {
colors: ["aaa", "bbb", "ccc"],
};

// 3. 子组件展示数据
class Son extends React.Component {
render() {
return (
<ul>
{this.props.colors.map((item, index) => {
return <li key={index}>{item}</li>;
})}
</ul>
);
}
}

// 类静态属性声明默认值
static defaultProps = {
colors: ["11111", "22222", "33333"],
};

生命周期

React Lifecycle Methods diagram

生命周期 - 挂载阶段

钩子 函数触发时机作用
constructor创建组件时,最先执行,初始化的时候只执行一次1. 初始化 state 2. 创建 Ref 3. 使用 bind 解决 this 指向问题等
render每次组件渲染都会触发渲染 UI(注意: 不能在里面调用 setState() , 会无限循环)
componentDidMount组件挂载(完成 DOM 渲染)后执行,初始化的时候执行一次1. 发送网络请求 2.DOM 操作
import React from "react";
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
class Parent extends React.Component {
render() {
return <div>hello world</div>;
}
}
root.render(
<>
<Parent />
</>
);

class Parent extends React.Component {
// 创建组件时,最先执行,初始化的时候只执行一次
constructor() {
super();
console.log("constructor...创建组件时,最先执行,初始化的时候只执行一次");
}
// 组件挂载(完成 DOM 渲染)后执行,初始化的时候执行一次
componentDidMount() {
console.log("componentDidMount...组件挂载(完成 DOM 渲染)后执行,初始化的时候执行一次");
}
// 每次组件渲染都会触发
render() {
console.log("render...每次组件渲染都会触发");
return <div>hello world</div>;
}
}

state = {
count: 0,
};

return <button onClick={this.handleClick}>{this.state.count}</button>;

handleClick = () => {
this.setState({
count: this.state.count + 1,
});
};

生命周期 - 更新阶段

钩子函数触发时机作用
render每次组件渲染都会触发渲染 UI(与 挂载阶段 是同一个 render)
componentDidUpdate组件更新后(DOM 渲染完毕)DOM 操作,可以获取到更新后的 DOM 内容,不要直接调用 setState
import React from "react";
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
class Parent extends React.Component {
state = {
count: 0,
};
// 创建组件时,最先执行,初始化的时候只执行一次
constructor() {
super();
console.log("constructor...创建组件时,最先执行,初始化的时候只执行一次");
}
// 组件挂载(完成 DOM 渲染)后执行,初始化的时候执行一次
componentDidMount() {
console.log("componentDidMount...组件挂载(完成 DOM 渲染)后执行,初始化的时候执行一次");
}
handleClick = () => {
this.setState({
count: this.state.count + 1,
});
};
// 每次组件渲染都会触发
render() {
console.log("render...每次组件渲染都会触发");
return <button onClick={this.handleClick}>{this.state.count}</button>;
}
}
root.render(
<>
<Parent />
</>
);
// 组件更新后(DOM 渲染完毕)
componentDidUpdate() {
console.log("componentDidUpdate...组件更新后(DOM 渲染完毕)");
}

生命周期 - 卸载阶段

钩子函数触发时机作用
componentWillUnmount组件卸载(从页面中消失)执行清理工作(比如:清理定时器等)
import React from "react";
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
class Son extends React.Component {
render() {
return "this is son";
}
}
class Parent extends React.Component {
render() {
return (
<div>
<Son />
</div>
);
}
}
root.render(
<>
<Parent />
</>
);

// 组件卸载(从页面中消失)
componentWillUnmount() {
console.log("componentWillUnmount...组件卸载(从页面中消失)");
}

state = {
flag: true,
};

handleClick = () => {
this.setState({
flag: false,
});
};

<div>
{this.state.flag ? <Son /> : ""}
<button onClick={this.handleClick}>click</button>
</div>

🚩 阶段小练习-todolist

todolist.zip

Terminal window
npm i
或者
yarn
Terminal window
npm start
或者
yarn start

Terminal window
npm run mock-serve
或者
yarn mock-serve
接口作用接口地址接口方法接口参数
获取列表http://localhost:3001/dataGET
删除http://localhost:3001/data/
DELETEid
搜索http://localhost:3001/data/?q=keywordGETname(以 name 字段搜索)

功能核心思路
表格数据渲染组件使用
删除功能获取当前 id 调用接口
搜索功能用的依旧是列表接口,多传一个 q 参数
清除搜索功能清空搜索参数 重新获取列表

import axios from "axios";
接口作用接口地址接口方法接口参数
获取列表http://localhost:3001/dataGET
删除http://localhost:3001/data/
DELETEid
搜索http://localhost:3001/data/?q=keywordGETname(以 name 字段搜索)
// 加载列表
loadList = async () => {
const res = await axios.get("http://localhost:3001/data");
console.log(res);
};
componentDidMount() {
this.loadList();
}

loadList = async () => {
const res = await axios.get("http://localhost:3001/data");
this.setState({
list: res.data,
});
};

rowKey={"id"}
render: (text, record) => (
<Space size="middle">
<Popconfirm title="确定要删除吗?" onConfirm={() => this.handleDelete(record.id)}>
<a href="#">删除</a>
</Popconfirm>
</Space>
),

handleDelete = () => {
alert("开始删除...");
};

handleDelete = (id) => {
alert("开始删除, id为" + id);
};

接口作用接口地址接口方法接口参数
获取列表http://localhost:3001/dataGET
删除http://localhost:3001/data/
DELETEid
搜索http://localhost:3001/data/?q=keywordGETname(以 name 字段搜索)
// 删除
handleDelete = async (id) => {
// 调用删除接口
await axios.delete("http://localhost:3001/data/" + id);
// 重新拉取列表
this.loadList();
};

// 搜索
onSearch = (value) => {
alert(value);
};

{
"data": [
{
"id": 1,
"name": "吃饭",
"des": "干饭人干饭魂"
},
{
"id": 2,
"name": "睡觉",
"des": "不如睡觉写代码不如睡觉"
},
{
"id": 3,
"name": "打豆豆",
"des": "不如睡觉写代码不如睡觉"
},
{
"id": 4,
"name": "写vue代码",
"des": "不如睡觉写代码不如睡觉"
}
]
}
接口作用接口地址接口方法接口参数
获取列表http://localhost:3001/dataGET
删除http://localhost:3001/data/
DELETEid
搜索http://localhost:3001/data/?q=keywordGETname(以 name 字段搜索)
// 搜索
onSearch = async (value) => {
const res = await axios.get("http://localhost:3001/data/?q=" + value);
this.setState({
list: res.data,
});
};

todolist.zip

什么是 hooks

Hooks 解决了什么问题

useState

react-demo.zip

import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
function App() {
return <div>这是一个函数组件</div>;
}
root.render(
<>
<App />
</>
);

// 1. 导入 `useState` 函数
import { useState } from "react";
function App() {
// 2. 调用 useState 函数,并传入状态的初始值, 返回值:数组,包含两个值:状态值和修改该状态的函数
const result = useState(0);
return <div>这是一个函数组件</div>;
}
// 3. 从 useState 函数的返回值中,拿到状态和修改状态的方法
const [count, setCount] = result;
return (
<div>
<button>增加</button>
{/* 4. 在 JSX 中展示状态 */}
<div>当前计数: {count}</div>
</div>
);
const handleClick = (number) => {
// 5. 调用修改状态的方法更新状态
setCount(number);
};

<button
onClick={() => {
handleClick(count + 1);
}}
>
增加
</button>

组件的更新过程

const [count, setCount] = useState(0);
const [list, setList] = useState([]);
// 1. 导入 `useState` 函数
import { useState } from "react";
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
function App() {
const [count, setCount] = useState(0);
const [list, setList] = useState([]);
const handleClick = () => {
// count 自增
setCount(count + 1);
// 生成一个新数组, 包含原来数组的值, 以及最新的count
const newList = [...list, count];
// 更新数组
setList(newList);
};
return (
<div>
<button onClick={handleClick}>增加</button>
{/* 在 JSX 中展示状态 */}
<div>当前计数: {count}</div>
<div>当前数组: {list.join("---")}</div>
</div>
);
}
root.render(
<>
<App />
</>
);

函数副作用

const arr = [6, 5, 4, 3, 2, 1];
const arr1 = arr.sort();
console.log(arr1);
console.log(arr);
const arr = [6, 5, 4, 3, 2, 1];
const arr1 = arr.slice().sort();
console.log(arr1);
console.log(arr);

useEffect

import { useState } from "react";
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
function App() {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount(count + 1)}>增加</button>
<div>{count}</div>
</div>
);
}
root.render(
<>
<App />
</>
);
// 导入 `useEffect` 函数
import { useEffect, useState } from "react";
// 2. 调用 useEffect 函数,并传入回调函数
useEffect(() => {
console.log("副作用执行了....");
});

useEffect(() => {
console.log("副作用执行了....");
// 3. 在回调函数中编写副作用处理(dom 操作)
document.title = count + "!!!!";
});

依赖项控制执行时机

// 导入 `useEffect` 函数
import { useEffect, useState } from "react";
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
function App() {
const [count, setCount] = useState(0);
// 2. 调用 useEffect 函数,并传入回调函数
useEffect(() => {
console.log("副作用执行了....");
// 3. 在回调函数中编写副作用处理(dom 操作)
document.title = count + "!!!!";
});
return (
<div>
<button onClick={() => setCount(count + 1)}>增加</button>
<div>{count}</div>
</div>
);
}
root.render(
<>
<App />
</>
);

useEffect(() => {
console.log("副作用执行了....");
// 3. 在回调函数中编写副作用处理(dom 操作)
document.title = count + "!!!!";
}, []);

// 导入 `useEffect` 函数
import { useEffect, useState } from "react";
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
function App() {
const [count, setCount] = useState(0);
const [name, setName] = useState("张三");
// 2. 调用 useEffect 函数,并传入回调函数
useEffect(() => {
// 3. 在回调函数中编写副作用处理(dom 操作)
console.log("副作用执行了....");
document.title = count + "!!!!";
}, [count]);
return (
<div>
<button onClick={() => setCount(count + 1)}>增加</button>
<div>{count}</div>
<button onClick={() => setName("李四")}>改名字</button>
<div>{name}</div>
</div>
);
}
root.render(
<>
<App />
</>
);

🚩 自定义 hook 小练习 1

import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
function App() {
return <div>这是一个组件</div>;
}
root.render(
<>
<App />
</>
);

// 引入 React 的 useState 钩子
import { useState } from "react";
// 定义一个名为 useWindowScroll 的自定义钩子函数
export function useWindowScroll() {
// 使用 useState 钩子创建一个状态变量 y 和设置状态的函数 setY,并将初始值设为 0
const [y, setY] = useState(0);
// 添加滚动事件监听器到 window 对象上
window.addEventListener("scroll", () => {
// 获取当前文档顶部相对于视口的垂直偏移量
const h = document.documentElement.scrollTop;
// 更新状态变量 y 的值为获取到的偏移量 h
setY(h);
});
// 返回状态变量 y,以便在组件中使用
return y;
}
import ReactDOM from "react-dom/client"; // 导入React DOM的客户端模块
import { useWindowScroll } from "./useWindowScroll"; // 导入自定义的useWindowScroll钩子函数
const root = ReactDOM.createRoot(document.getElementById("root")); // 创建根节点
function App() {
const y = useWindowScroll(); // 使用useWindowScroll钩子获取滚动位置
return <div style={{ height: "12000px" }}>{y}</div>; // 返回一个包含滚动位置的div元素
}
root.render(
<>
<App /> // 渲染App组件
</>
);

🚩 自定义 hook 小练习 2

import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
function App() {
return <div>这是一个组件</div>;
}
root.render(
<>
<App />
</>
);

import { useEffect, useState } from "react";
export function useLocalStorage(key, defaultValue) {
const [message, setMessage] = useState(defaultValue);
// 每次只要message变化 就会自动同步到本地localStorage
useEffect(() => {
window.localStorage.setItem(key, message);
}, [message, key]);
return [message, setMessage];
}

import ReactDOM from "react-dom/client"; // 导入ReactDOM模块中的createRoot函数
import { useLocalStorage } from "./useLocalStorage"; // 导入自定义的useLocalStorage钩子函数
const root = ReactDOM.createRoot(document.getElementById("root")); // 创建根节点
function App() {
const [message, setMessage] = useLocalStorage("username", "zhangsan"); // 使用useLocalStorage钩子,获取本地存储的值并设置默认值为"zhangsan"
const changeName = () => {
setMessage("lisi"); // 点击按钮后,修改message的值为"lisi"
};
return (
<>
{/* 在页面上显示message的值 */}
<div>{message}</div>
{/* 点击按钮执行changeName函数,修改message的值 */}
<button onClick={changeName}>改名字</button>
</>
);
}
root.render(
<>
{/* 渲染App组件 */}
<App />
</>
); // 将根节点中渲染的内容进行更新操作

useState - 回调函数作为参数

const [name, setName] = useState(() => {
// 编写计算逻辑 return '计算之后的初始值'
});

import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
function Counter(props) {
const { count } = props;
return (
<div>
<button>{count}</button>
</div>
);
}
root.render(
<>
<Counter count={10} />
<Counter count={20} />
</>
);

import { useState } from "react";
// ...
const [count, setCount] = useState(() => {
// 使用函数的返回值, 作为初始值
return props.count;
});

<button onClick={() => setCount(count + 1)}>{count}</button>

import ReactDOM from "react-dom/client";
import { useState } from "react";
const root = ReactDOM.createRoot(document.getElementById("root"));
function Counter(props) {
const [count, setCount] = useState(() => {
// 使用函数的返回值, 作为初始值
return props.count;
});
return (
<div>
<button onClick={() => setCount(count + 1)}>{count}</button>
</div>
);
}
root.render(
<>
<Counter count={10} />
<Counter count={20} />
</>
);

useEffect - 清理副作用

useEffect(() => {
console.log("副作用函数执行了");
// 副作用函数的执行时机为: 在下一次副作用函数执行之前执行
return () => {
console.log("清理副作用的函数执行了");
// 在这里写清理副作用的代码
};
});
import { useEffect, useState } from "react";
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
function Son() {
useEffect(() => {
// 函数副作用
setInterval(() => {
console.log("副作用函数执行了");
}, 1000);
});
return <div>this is son</div>;
}
function Parent() {
const [flag, setFlag] = useState(true);
return (
<>
<button onClick={() => setFlag(!flag)}>click</button>
{flag ? <Son /> : null}
</>
);
}
root.render(
<>
<Parent />
</>
);
useEffect(() => {
const timeId = setInterval(() => {
console.log("副作用执行了...");
}, 1000);
// 清理副作用的代码, 组件销毁后会自动触发
return () => {
clearInterval(timeId);
};
});

useEffect - 发送网络请求

{
"code": "0000",
"data": [
{
"id": 1,
"name": "全部商品-番茄150g/份",
"imgUrl": "https://markdown-1253389072.cos.ap-nanjing.myqcloud.com/202202261527635.png",
"sales": 1,
"promotionPrice": 1.6,
"originalPrice": 1.6
},
{
"id": 2,
"name": "全部商品-番茄250g/份",
"imgUrl": "https://markdown-1253389072.cos.ap-nanjing.myqcloud.com/202202261527853.png",
"sales": 2,
"promotionPrice": 2.6,
"originalPrice": 2.6
},
{
"id": 3,
"name": "全部商品-番茄350g/份",
"imgUrl": "https://markdown-1253389072.cos.ap-nanjing.myqcloud.com/202202261527922.png",
"sales": 3,
"promotionPrice": 3.6,
"originalPrice": 3.6
},
{
"id": 4,
"name": "全部商品-番茄450g/份",
"imgUrl": "https://markdown-1253389072.cos.ap-nanjing.myqcloud.com/202202261530259.png",
"sales": 4,
"promotionPrice": 4.6,
"originalPrice": 4.6
}
],
"desc": "成功"
}
useEffect(async () => {
const res = await axios.get("http://xxx.com/demo");
console.log(res);
}, []);
useEffect(() => {
async function fetchData() {
const res = await axios.get("http://xxx.com/demo");
console.log(res);
}
fetchData();
}, []);
Terminal window
npm install axios
或者
yarn add axios
import axios from "axios";
import { useEffect, useState } from "react";
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
function App() {
useEffect(() => {
async function getData() {
const response = await axios.get(
"https://www.fastmock.site/mock/65fd811567052bc43a50412985949ab6/api/shop/1/tab/all"
);
console.log(response);
}
getData();
}, []);
return <></>;
}
root.render(
<>
<App />
</>
);
Terminal window
npm run start

useEffect(async () => {
const response = await axios.get(
"https://www.fastmock.site/mock/65fd811567052bc43a50412985949ab6/api/shop/1/tab/all"
);
console.log(response);
}, []);

useRef

import React from "react";
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
class Son extends React.Component {
render() {
return <div>this is son</div>;
}
}
function Parent() {
return (
<div>
<Son />
<h1>this is h1</h1>
</div>
);
}
root.render(
<>
<Parent />
</>
);

// 1. 导入 useRef 函数
import { useRef } from "react";
// 2. 执行 useRef 函数并传入 null,返回值为一个对象, 内部有一个 current 属性存放拿到的 dom 对象(组件实例)
const sonRef = useRef(null);
const h1Ref = useRef(null);

import React, { useEffect, useRef } from "react";
useEffect(() => {
console.log(sonRef.current);
console.log(h1Ref.current);
});

useContext

import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
function ComponentA() {
return (
<div>
this is component A
<ComponentC />
</div>
);
}
function ComponentC() {
return <div>this is component C</div>;
}
function Parent() {
return (
<div>
<ComponentA />
</div>
);
}
root.render(
<>
<Parent />
</>
);

import { createContext } from "react";
// ...
// 1. 使用`createContext` 创建 Context 对象
const Context = createContext();
import { createContext, useState } from "react";
function Parent() {
const [count] = useState(0);
return (
// 在顶层组件通过`Provider` 提供数据
<Context.Provider value={count}>
<div>
<ComponentA />
</div>
</Context.Provider>
);
}
import { createContext, useContext, useState } from "react";
function ComponentC() {
// 3. 在底层组件通过`useContext`函数获取数据
const count = useContext(Context);
return <div>this is component C, count is : {count}</div>;
}

function Parent() {
const [count, setCount] = useState(0);
return (
// 2. 在顶层组件通过 Provider 提供数据
<Context.Provider value={count}>
<div>
<button
onClick={() => {
setCount(count + 1);
}}
>
click
</button>
<ComponentA />
</div>
</Context.Provider>
);
}

React-Router 前置知识

单页面应用 SPA

路由的本质

前端路由 vs 后端路由

准备路由项目环境

vite 简介

Vite

Vite 中文网

搭建一个 Vite 项目

Terminal window
npm create vite@latest
或者
yarn create vite

Terminal window
npm i
或者
yarn

Terminal window
npm run dev
或者
yarn dev

安装 react-router

Terminal window
npm i react-router-dom
或者
yarn add react-router-dom

基础使用

// 引入必要的内置组件
import { BrowserRouter, Routes, Route, Link } from "react-router-dom";
// 准备两个路由组件
const Home = () => <div>home</div>;
const About = () => <div>about</div>;
function App() {
return (
<div className="App">
<BrowserRouter>
<Link to="/">
<button>首页</button>
</Link>
<Link to="/about">
<button>关于</button>
</Link>
<Routes>
<Route path="/" element={<Home />}></Route>
<Route path="/about" element={<About />}></Route>
</Routes>
</BrowserRouter>
</div>
);
}
export default App;

// 引入必要的内置组件
import { BrowserRouter, Routes, Route, Link } from "react-router-dom";
// 准备两个路由组件
const Home = () => <div>home</div>;
const About = () => <div>about</div>;
function App() {
return (
<div className="App">
{/* 声明当前要用一个非hash模式的路由 */}
<BrowserRouter>
{/* 指定跳转的组件, to用来配置路由地址 */}
<Link to="/">
<button>首页</button>
</Link>
<Link to="/about">
<button>关于</button>
</Link>
{/* 路由出口, 路由对应的组件会在这里进行渲染 */}
<Routes>
{/* 指定路径和组件的对应关系, path代表路径, element代表组件, 成对出现, path ==> element */}
<Route path="/" element={<Home />}></Route>
<Route path="/about" element={<About />}></Route>
</Routes>
</BrowserRouter>
</div>
);
}
export default App;

核心内置组件说明

BrowerRouter

模式实现方式路由 url 表现
HashRouter监听 url hash 值实现http://localhost:3000/#/about
BrowserRouterh5 的 history.pushState API 实现http://localhost:3000/about
// 引入必要的内置组件
import { Routes, Route, Link, HashRouter } from "react-router-dom";
// 准备两个路由组件
const Home = () => <div>home</div>;
const About = () => <div>about</div>;
function App() {
return (
<div className="App">
{/* 生命当前要用一个非hash模式的路由 */}
<HashRouter>
{/* 指定跳转的组件, to用来配置路由地址 */}
<Link to="/">
<button>首页</button>
</Link>
<Link to="/about">
<button>关于</button>
</Link>
{/* 路由出口, 路由对应的组件会在这里进行渲染 */}
<Routes>
{/* 指定路径和组件的对应关系, path代表路径, element代表组件, 成对出现, path ==> element */}
<Route path="/" element={<Home />}></Route>
<Route path="/about" element={<About />}></Route>
</Routes>
</HashRouter>
</div>
);
}
export default App;

Routes

Route

编程式导航

function Login() {
return <div>login</div>;
}
export default Login;
src\App.jsx
import Login from "./Login";
src\App.jsx
<Route path="/login" element={<Login />}></Route>

src\Login.jsx
// 导入useNavigate
import { useNavigate } from "react-router-dom";

src\Login.jsx
// 执行useNavigate, 得到一个跳转函数
const navigate = useNavigate();

src\Login.jsx
// 跳转到首页
function goHome() {
// 调用跳转函数, 传入目标路径
navigate("/");
}

src\Login.jsx
<button onClick={goHome}>跳转首页</button>

src\Login.jsx
function goHome() {
// 调用跳转函数, 传入目标路径
navigate("/", { replace: true });
}

路由传参

searchParams 传参

src\Login.jsx
function goHome() {
// 调用跳转函数, 传入目标路径, searchParams传参
navigate("/?id=10001", { replace: true });
}
src\App.jsx
const Home = () => {
// 路由取参
const [params] = useSearchParams();
// params有一个get方法, 可以获取参数
const id = params.get("id");
return <div>home 用户id {id}</div>;
};

params 传参

src\Login.jsx
function goHome() {
// 调用跳转函数, 传入目标路径, searchParams传参
navigate("/10001", { replace: true });
}
src\App.jsx
const Home = () => {
let params = useParams();
const id = params.id;
return <div>home 用户id是 {id}</div>;
};

嵌套路由

src\Layout.jsx
import { Outlet } from "react-router-dom";
function Layout() {
return <div>layout</div>;
}
export default Layout;
src\Board.jsx
function Board() {
return <div>Board</div>;
}
export default Board;
src\Article.jsx
function Article() {
return <div>Article</div>;
}
export default Article;
src\App.jsx
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Article from "./Article";
import Board from "./Board";
import Layout from "./Layout";
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Layout></Layout>}>
<Route path="board" element={<Board></Board>}></Route>
<Route path="article" element={<Article></Article>}></Route>
</Route>
</Routes>
</BrowserRouter>
);
}
export default App;
src\Layout.jsx
import { Outlet } from "react-router-dom";
function Layout() {
return (
<div>
layout
{/* 二级路由的出口 */}
<Outlet></Outlet>
</div>
);
}
export default Layout;

默认二级路由

src\App.jsx
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Article from "./Article";
import Board from "./Board";
import Layout from "./Layout";
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Layout></Layout>}>
<Route index element={<Board></Board>}></Route>
<Route path="article" element={<Article></Article>}></Route>
</Route>
</Routes>
</BrowserRouter>
);
}
export default App;

404 路由配置

src\NotFound.jsx
function NotFound() {
return <div>NotFound</div>;
}
export default NotFound;
src\App.jsx
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Article from "./Article";
import Board from "./Board";
import Layout from "./Layout";
import NotFound from "./NotFound";
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Layout></Layout>}>
<Route index element={<Board></Board>}></Route>
<Route path="article" element={<Article></Article>}></Route>
</Route>
{/* 如果没有匹配成功, 走这个路由 */}
<Route path="*" element={<NotFound />}></Route>
</Routes>
</BrowserRouter>
);
}
export default App;

集中式路由配置

src\App.jsx
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Article from "./Article";
import Board from "./Board";
import Layout from "./Layout";
import NotFound from "./NotFound";
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Layout></Layout>}>
<Route index element={<Board></Board>}></Route>
<Route path="article" element={<Article></Article>}></Route>
</Route>
{/* 如果没有匹配成功, 走这个路由 */}
<Route path="*" element={<NotFound />}></Route>
</Routes>
</BrowserRouter>
);
}
export default App;
src\Layout.jsx
import { Outlet } from "react-router-dom";
function Layout() {
return (
<div>
layout
{/* 二级路由的出口 */}
<Outlet></Outlet>
</div>
);
}
export default Layout;
src\Board.jsx
function Board() {
return <div>Board</div>;
}
export default Board;
src\Article.jsx
function Article() {
return <div>Article</div>;
}
export default Article;
src\NotFound.jsx
function NotFound() {
return <div>NotFound</div>;
}
export default NotFound;
src\App.jsx
import { BrowserRouter, Route, Routes, useRoutes } from "react-router-dom";
import Article from "./Article";
import Board from "./Board";
import Layout from "./Layout";
import NotFound from "./NotFound";
// 1. 准备一个路由数组 数组中定义所有的路由对应关系
const routesList = [
{
path: "/",
element: <Layout />,
children: [
{
element: <Board />,
index: true, // index设置为true 变成默认的二级路由
},
{
path: "article",
element: <Article />,
},
],
},
// 增加n个路由对应关系
{
path: "*",
element: <NotFound />,
},
];
// 2. 使用useRoutes方法传入routesList生成Routes组件
function WrapperRoutes() {
let element = useRoutes(routesList);
return element;
}
function App() {
return (
<BrowserRouter>
{/* 3. 替换之前的Routes组件 */}
<WrapperRoutes />
</BrowserRouter>
);
}
export default App;

完结撒花