React学习
01 -《yarn 包管理器》
一、包管理器
二、安装配置 yarn
1、全局安装 yarn
css
npm i -g yarn
2、查看版本号
yarn -v
3、配置镜像地址
将原本的下载地址,更换为国内的地址:
arduino
yarn config set registry https://registry.npmmirror.com
三、常用命令
1、下载局部依赖包
(1)下载局部依赖包(最新版)
csharp
yarn add 依赖包名称
(2)下载多个局部依赖包
csharp
yarn add 依赖包名称一 依赖包名称二
(3)下载指定版本依赖包
csharp
yarn add 依赖包名称@版本号
(4)下载开发环境所需依赖包
开发环境,指的项目还未上线,还在开发过程中。以上3个命令,下载的都是开发和生产都需要的依赖包。
如果只在开发时需要,生产(上线)后不需要:
sql
yarn add 依赖包名称 -D# 或yarn add 依赖包名称 --dev
(5)下载项目所有依赖包
根据项目的 package.json
文件,来下载项目所需要的所有的依赖包:
yarn
2、下载全局依赖包
csharp
yarn global add 依赖包名称
3、卸载依赖包
(1)卸载局部依赖包
arduino
yarn remove 依赖包名称
(2)卸载全局依赖包
csharp
yarn global remove 依赖包名称
02 -《搭建 React 项目》
一、搭建方式的选择
- React 官方脚手架:Create React App,简称 CRA,底层用的 webpack。
- Vite:Vue 的团队研发的,用来代替 webpack。
二、CRA 搭建 React 项目
1、创建项目
lua
npx create-react-app react-demo
其中,react-demo
是项目名称,可自行定义。npx 临时安装,以上命令执行时会先安装 CRA 脚手架工具,然后再用工具创建项目。等到项目创建完成后,会自动删除 CRA 脚手架工具。
2、启动项目
项目的启动命令,应该在项目的 package.json
文件中,找到 scripts
配置,通常项目的启动命令在第一个。
项目的启动命令,如果用 npm:
arduino
npm run 启动名称
特殊情况:如果启动名称叫 start
,可以省略 run
。
如果用 yarn:
yarn 启动名称
如果想要更换启动命令的名称,也可以在 package.json
文件中自己替换。
例如我们将 React 的 start 命令换成 dev 命令:
json
{
"scripts": {
"dev": "react-scripts start"
// ...
},
}
更换后,React 项目的启动命令就变成了:
yarn dev
三、删除多余文件和代码
1、删除多余文件
src 中只需要保留一下两个文件即可:
lua
src
|--- App.js
|--- index.js
2、删除 index.js 中的多余代码
javascript
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<App />
);
3、删除 App.js 中的多余代码
javascript
function App() {
return (
<div>
</div>
);
}
export default App;
四、安装 VSCode 插件

03 -《虚拟节点》
一、虚拟节点的概念
虚拟节点,本质上是一个 JS 对象,通过 JS 对象身上的属性,来描述当前的节点,例如节点的名称、节点的属性、节点的样式...
类似:
arduino
{
tagName: 'div',
children: '你好',
// ...
}
二、创建虚拟节点
在 React 中,我们可以直接在 JS 中编写 HTML 代码:
ini
const hello = <h1 id='hi'>你好</h1>;
console.log(hello);
通过这种方式创建的 HTML 标签,实际上就是一个虚拟节点。
而这种创建虚拟节点的方式,其底层真正的实现,是通过 React.createElement()
方法来完成的:
react
// 真正创建虚拟节点的方式
const hello = React.createElement('h1');
console.log(hello);
04 -《React 组件》
一、组件的分类
React 组件的分为两类:
- 函数组件:以函数的形式来创建组件;
- 类组件:以类 class 的形式来创建组件;
1、函数组件
在 VSCode 中,可以通过快捷方式来创建函数组件:
(1)rfc
react
import React from 'react'
export default function App() {
return (
<div>App</div>
)
}
(2)rfce
react
import React from 'react'
function App() {
return (
<div>App</div>
)
}
export default App
(3)rafce
react
import React from 'react'
const App = () => {
return (
<div>App</div>
)
}
export default App
语法要求:
- 函数中必须有 return 返回值,且返回指定的内容是 HTML 标签;
- 函数名(组件名)首字母必须大写;
2、类 class 组件
rcc:
react
import React, { Component } from 'react'
export default class App extends Component {
render() {
return (
<div>App</div>
)
}
}
二、创建组件文件
- 在 React 组件中,文件可以是
.js
也可以是.jsx
,但是更推荐.jsx
; - 文件名首字母可以不大写,但是文件中的组件名首字母必须大写。
三、引入渲染子组件
react
import React, { Component } from 'react'
import ClassComponent from './01-组件基础/ClassComponent'
import FunctionComponent from './01-组件基础/FunctionComponent'
export default class App extends Component {
render() {
return (
<div>
<h1>App组件</h1>
<ClassComponent />
<FunctionComponent />
</div>
)
}
}
05 -《JSX 基础语法》
一、JSX 概念
JSX 全称,JavaScript XML。
JSX 语法,指的就是可以将 HTML 标签和 JS 代码混在一起编写。
二、JSX 基础语法
1、JSX 根节点
所有的组件中,都必须有一个最外层的根节点:
react
import React, { Component } from 'react'
export default class JSXComponent extends Component {
render() {
return (
<div>
<h1></h1>
<h1></h1>
<h1></h1>
<h1></h1>
</div>
)
}
}
根节点可以用空标签来代替:
react
export default class JSXComponent extends Component {
render() {
return (
<>
<h1></h1>
<h1></h1>
<h1></h1>
<h1></h1>
</>
)
}
}
2、标签的特殊属性
由于 class 和 for 这两个关键字在 JS 中有其他的作用,所以标签身上的 class 和 for 属性需要替换成 className
和 htmlFor
:
react
export default class JSXComponent extends Component {
render() {
return (
<>
<h1 className='hello'>你好</h1>
<label htmlFor="username"></label>
</>
)
}
}
3、动态渲染数据
react
const name = '张三';
export default class JSXComponent extends Component {
render() {
return (
<>
<h1 className='hello'>你好, {name}</h1>
</>
)
}
}
4、动态渲染属性
react
const link = 'https://www.baidu.com'
export default class JSXComponent extends Component {
render() {
return (
<>
<a href={link}>百度一下</a>
</>
)
}
}
5、数学运算
react
const num1 = 100;
const num2 = 200;
export default class JSXComponent extends Component {
render() {
return (
<>
<h2>{num1 + num2}</h2>
</>
)
}
}
6、三目运算
react
const age = 17;
export default class JSXComponent extends Component {
render() {
return (
<>
<h3>{age >= 18 ? '已成年' : '未成年'}</h3>
</>
)
}
}
7、函数调用
react
const num1 = 100;
const num2 = 200;
const getSum = () => {
// 计算过程
return num1 + num2;
}
export default class JSXComponent extends Component {
render() {
return (
<>
<h2>{getSum()}</h2>
</>
)
}
}
06 -《JSX 列表渲染》
一、JSX 渲染数组
React 的 JSX 语法,可以直接解析数组,将数组中的元素提取出来,进行渲染。
react
const arr = [
<li>张三</li>,
<li>李四</li>,
<li>王五</li>
]
export default class ForComponent extends Component {
render() {
return (
<ul>
{arr}
</ul>
)
}
}
所以,只要我们将纯数据的数组,处理成带有节点的数组,就可以使用数组的渲染渲染。
二、JSX 的列表渲染
react
const arr = [ '张三', '李四', "王五"]
export default class ForComponent extends Component {
render() {
return (
<ul>
{
arr.map((item, index) => {
return <li key={index}>{item}</li>
})
}
</ul>
)
}
}
说明:React 中循环产生的元素身上,需要通过 key 属性来设置一个唯一值。
三、循环中的条件判断
在使用 map 循环渲染数据节点时,还可以通过 if 来进行条件判断:
react
const movieTypes = [
{ id: 1, name: '全部' },
{ id: 2, name: '现代' },
{ id: 3, name: '谍战' },
{ id: 4, name: '古装' },
]
export default class ForComponent extends Component {
render() {
return (
<div>
{
movieTypes.map(item => {
if (item.id == 1) {
return <strong key={item.id}>{item.name}</strong>
}
return <span key={item.id}>{item.name}</span>
})
}
</div>
)
}
}
07 -《JSX 条件渲染》
一、if 条件渲染
二、三元运算
react
const movieTypes = [
{ id: 1, name: '全部' },
{ id: 2, name: '现代' },
{ id: 3, name: '谍战' },
{ id: 4, name: '古装' },
]
export default class ForComponent extends Component {
render() {
return (
<div>
{
movieTypes.map(item => {
return item.id == 1 ? (
<strong key={item.id}>{item.name}</strong>
) : (
<span key={item.id}>{item.name}</span>
)
})
}
</div>
)
}
}
三、|| 或运算符
A || B
,如果 A 为真,留下 A,如果 A 为假,留下 B。
四、&& 与运算符
A && B
,如果 A 为真,留下 B,如果 A 为假,留下 A。
08 -《组件内部数据》(类组件)
一、定义数据
react
export default class StateComponent extends Component {
state = {
count: 1,
student: {
name: '张三',
age: 20
}
}
render() {
return (
<div>StateComponent</div>
)
}
}
二、使用数据
1、直接使用(不解构)
react
export default class StateComponent extends Component {
state = {
count: 1,
student: {
name: '张三',
age: 20
}
}
render() {
console.log(this.state.count);
return (
<div>
<h1>{this.state.count}</h1>
<h1>姓名:{this.state.student.name}</h1>
</div>
)
}
}
2、解构后再使用
react
export default class StateComponent extends Component {
state = {
count: 1,
student: {
name: '张三',
age: 20
}
}
render() {
// const { count, student: { name, age } } = this.state;
const { count, student } = this.state;
const { name, age } = student;
return (
<div>
<h1>{count}</h1>
<h1>姓名:{student.name}</h1>
<h1>年龄:{age}</h1>
</div>
)
}
}
三、修改数据
React 中提供了 this.setState()
来修改数据,只有通过这种方式修改的数据,页面才会更新。
1、修改基本类型数据
react
export default class StateComponent extends Component {
state = {
count: 1
}
render() {
return (
<div>
<button onClick={() => {
this.setState({
count: this.state.count + 1
})
}}>count+1</button>
</div>
)
}
}
2、修改对象
react
export default class StateComponent extends Component {
state = {
student: {
name: '李四',
age: 20,
gender: '男'
}
}
render() {
return (
<div>
<button onClick={() => {
this.setState({
student: {
...this.state.student, // 保留其他不变的数据
age: this.state.student.age + 1
}
})
}}>age+1</button>
</div>
)
}
}
四、异步的 setState
setState 方法是一个异步方法,如果想要在数据修改完成后再执行一些其他的操作,可以使用 setState 的第二个参数:
react
export default class StateComponent extends Component {
state = {
count: 1
}
render() {
return (
<div>
<button onClick={() => {
this.setState({
count: this.state.count + 1
}, () => {
console.log(this.state.count)
})
}}>count+1</button>
</div>
)
}
}
09 -《组件的事件》(类组件)
一、绑定事件
react
export default class EventComponent extends Component {
render() {
return (
<button onClick={() => {
console.log(1111);
}}>按钮</button>
)
}
}
二、事件方法(事件处理函数)
react
export default class EventComponent extends Component {
sayHello = () => {
console.log('hello');
}
render() {
return (
<button onClick={this.sayHello}>按钮二</button>
)
}
}
三、事件传参
react
export default class EventComponent extends Component {
sayHi = (name) => {
console.log('hi', name);
}
render() {
return (
<div>
<button onClick={() => {
this.sayHi('张三');
}}>按钮三</button>
<button onClick={() => this.sayHi('李四')}>按钮四</button>
</div>
)
}
}
四、事件对象
react
export default class EventComponent extends Component {
linkTo = (event) => {
event.preventDefault();
}
render() {
return (
<div>
<a href="" onClick={(event) => {
event.preventDefault();
}}>按钮五</a>
<a href="" onClick={this.linkTo}>按钮六</a>
<a href="" onClick={(event) => {
event.preventDefault();
this.linkTo()
}}>按钮七</a>
</div>
)
}
}
10 -《state 操作总结》(类组件)
一、数组删除
参考语法:
react
this.setState({
数组名: this.state.数组.filter(item => /* 筛选条件 */)
})
参考案例:
react
this.setState({
goodsData: this.state.goodsData.filter(item => item.id != id)
})
二、数组修改
参考语法:
react
this.setState({
数组名: this.state.数组.map(item => {
if(/* 判断条件,找到需要修改的 item */) {
return {
...item,
要修改的属性名: 新的值
}
}
return item;
})
})
参考案例:
react
this.setState({
goodsData: this.state.goodsData.map(item => {
if(item.id == id) {
return {
...item,
count: item.count - 1
}
}
return item;
})
})
三、数组新增
参考语法:
react
this.setState({
数组名: [
...this.state.数组,
新的值
]
})
参考案例:
react
this.setState({
goodsData: [
...this.state.goodsData,
{
id: 4,
name: '',
count: 100,
price: 10
}
]
})
四、对象属性的修改或新增
参考语法:
react
this.setState({
对象名: {
...this.state.对象,
修改或新增的属性名: 新的值
}
})
参考案例:
react
state = {
pageData: {
currentPage: 1,
pageSize: 10
}
}
this.setState({
pageData: {
...this.state.pageData,
currentPage: this.state.pageData.currentPage + 1
}
})
11 -《组件的样式》
一、样式的分类
- 内联样式:标签身上通过
style
属性设置样式 - 外部全局样式:不管在哪一个组件中引入,都会作用于所有组件;
- 外部局部样式:在哪一个组件中引入,只作用于当前组件;
二、样式的使用
1、内联样式
react
export default class StyleComponent extends Component {
render() {
return (
<div>
<h1 style={{ color: 'red', backgroundColor: '#eee' }}>你好</h1>
<div style={{ backgroundColor: 'green', width: 100, height: 100 }}></div>
</div>
)
}
}
2、外部全局样式
外部创建 .css
文件,所有的 CSS 选择器和 CSS 样式都可以正常使用。
在任意组件中引入该文件即可:
arduino
import './index.css'
3、外部局部样式
外部局部样式有以下几个要求:
- 文件名必须是
xxx.module.css
; - 所有的选择器只能使用 class 类选择器;
- 引入时需要通过
import 变量名 from '路径'
来引入; - 使用时通过
变量名.选择器名
来使用;
react
import styles from './index.module.css'
export default class StyleComponent extends Component {
render() {
return (
<h1 className={styles.title}>标题</h1>
)
}
}
12 -《生命周期》(类组件)
生命周期官网图:https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
一、分类
React 的生命周期分为三个阶段:
- 挂载阶段:
- 更新阶段:
- 销毁阶段:
二、常用的生命周期函数
1、挂载阶段
- constructor
- render
- componentDidMount:发送网络请求
2、更新阶段
- render
- componentDidUpdate
3、销毁阶段
- componentWillUnmount
三、不常用的生命周期函数
1、更新阶段:shouldComponentUpdate
react
export default class LifeCycle extends Component {
state = {
count: 1
}
shouldComponentUpdate(props, state) {
if (this.state.count == state.count) {
return false;
}
return true;
}
render() {
return (
<div>
<button onClick={() => {
this.setState({
count: 1
});
}}>按钮</button>
</div>
)
}
}
2、PureComponent
scala
import React, { PureComponent } from 'react'
export default class LifeCycle extends PureComponent {
state = {
count: 1
}
render() {
return (
<div>
<button onClick={() => {
this.setState({
count: 1
});
}}>按钮</button>
</div>
)
}
}
13 -《函数组件 Hook》
一、函数组件和类组件
React 16.8 版本之前,函数组件没有内部状态,也没有生命周期,所以更多的只是用函数组件来做展示组件。但是从 16.8 开始,React 新增了 Hook,让函数组件的功能得到了增强,有了自己的内部状态、生命周期等。
二、Hook 介绍
Hook,实际上就是 React 内部封装好的一组方法。我们通过调用这些 Hook 方法,就可以来设置函数组件内部的状态、生命周期等。
例如:
Hook 方法 | 作用 |
---|---|
useState() |
定义函数组件内部状态(数据) |
useEffect() |
模拟函数组件的生命周期 |
useMemo() |
缓存数据,模拟计算属性 |
... |
hook 使用规则
- 所有的 Hook 方法命名都是以
use
开头,以后我们自己定义的普通变量、函数等,命名要避开use
; - 所有的 Hook 方法在使用前,都必须先引入;
- 所有的 Hook 方法都只能在函数组件中使用,不能再嵌套在其他的条件语句、循环语句中;
14 -《函数组件 useState》
一、定义数据
react
const StateComponent = () => {
const [count] = useState(0);
const [students] = useState([
{ id: 1, name: '张三', age: 20 },
{ id: 2, name: '李四', age: 20 },
])
return (
<div></div>
)
}
useState()
的参数,就是初始数据。一个组件中,可以无限次的使用 useState
。
二、使用数据
函数组件中,数据的渲染和类组件一致:
react
const StateComponent = () => {
const [count] = useState(0);
const [students] = useState([
{ id: 1, name: '张三', age: 20 },
{ id: 2, name: '李四', age: 20 },
])
return (
<div>
<h1>{count}</h1>
<ul>
{
students.map(item => <li key={item.id}>{item.name}</li>)
}
</ul>
</div>
)
}
三、修改数据
useState
的返回值中,第一个是数据,第二个是修改数据的方法,通常以 set
开头来命名:
react
const StateComponent = () => {
const [count, setCount] = useState(0);
const [students, setStudents] = useState([
{ id: 1, name: '张三', age: 20 },
{ id: 2, name: '李四', age: 20 },
])
console.log(count); // 查看初始数据,和每次修改后的数据
return (
<div>
<h1>{count}</h1>
<button onClick={() => {
setCount(count + 1);
}}>count+1</button>
<ul>
{
students.map(item => <li key={item.id}>{item.name}</li>)
}
</ul>
<button onClick={() => {
setStudents(students.map(item => {
if (item.id == 2) {
return {
...item,
age: 21
}
}
return item;
}));
}}>修改李四年龄</button>
</div>
)
}
语法要求:
- 不能直接修改原数据;
- 调用对应的
set
方法,传递新数据,来实现旧数据的修改; set
方法是异步方法,因此如果想要查看修改后的数据,直接在顶层输出即可。
15 -《useMemo》
一、基础语法
useMemo,可以用来缓存数据,通常当我们需要通过计算旧数据来得到一个新数据时,就可以使用 useMemo 来模拟计算属性。
react
import React, { useMemo } from 'react'
const 变量 = useMemo(() => {
// 计算过程
return 值;
}, [依赖的旧数据])
二、示例代码
react
import React, { useMemo, useState } from 'react'
const ShoppingCart = () => {
const [goodsData, setGoodsData] = useState([
{ id: 1, name: '衣服', price: 100, count: 0 },
{ id: 2, name: '裤子', price: 200, count: 50 },
{ id: 3, name: '鞋子', price: 300, count: 20 },
]);
// 计算属性 totalPrice
const totalPrice = useMemo(() => {
return goodsData.reduce((prev, item) => {
return item.price * item.count + prev;
}, 0)
}, [goodsData]);
return (
<div>
<table>
...
</table>
<div>合计:{totalPrice}元</div>
</div>
)
}
export default ShoppingCart
16 -《表单操作》(受控组件和非受控组件)
一、分类
- 受控组件:表单元素完全受 React 的控制,不受用户控制。
- 非受控组件:表单元素不受 React 的控制,受用户控制。
二、非受控组件
1、设置默认值
输入框如果要作为非受控组件,我们需要用 defaultValue
来设置输入框的默认值。
单复选框如果要作为非受控组件,我们需要用 defaultChekced
来设置单复选框的默认选中。
react
const FormComponent = () => {
return (
<input type="text" defaultValue="hello" />
)
}
export default FormComponent
2、获取表单元素的值
如果要获取非受控组件的值,需要通过 ref 属性来绑定元素节点,通过获取元素节点,来得到节点中值:
javascript
import React, { useRef } from 'react'
const FormComponent = () => {
const inputRef = useRef(null);
console.log(inputRef); // { current: null }
return (
<div>
<input type="text" ref={inputRef} />
<button onClick={() => {
console.log('获取输入框节点', inputRef.current);
}}>搜索</button>
</div>
)
}
export default FormComponent
三、受控组件
1、输入框
react
import React, { useState } from 'react'
const FormComponent = () => {
const [msg, setMsg] = useState('');
return (
<input type="text" value={msg} onChange={(e) => {
setMsg(e.target.value)
}} />
)
}
2、下拉列表
react
import React, { useState } from 'react'
const FormComponent = () => {
const [city, setCity] = useState('');
return (
<select value={city} onChange={(e) => {
setCity(e.target.value);
}}>
<option value="四川">四川</option>
<option value="贵州">贵州</option>
<option value="云南">云南</option>
</select>
)
}
export default FormComponent
3、单选按钮
react
import React, { useState } from 'react'
const FormComponent = () => {
const [gender, setGender] = useState('');
return (
<div>
<input type="radio" checked={gender == '男'} onChange={() => {
setGender('男');
}} />
<label>男</label>
<input type="radio" checked={gender == '女'} onChange={() => {
setGender('女');
}} />
<label>女</label>
</div>
)
}
export default FormComponent
4、单个复选框
react
import React, { useState } from 'react'
const FormComponent = () => {
const [isAgree, setIsAgree] = useState(false);
return (
<div>
<input type="checkbox" checked={isAgree} onChange={() => {
setIsAgree(!isAgree);
}} />
<label>我已阅读并同意以上协议</label>
</div>
)
}
export default FormComponent
5、多个复选框
react
import React, { useState } from 'react'
const FormComponent = () => {
const [likes, setLikes] = useState(['睡觉']);
return (
<div>
<input type="checkbox" checked={likes.includes('吃饭')} onChange={(e) => {
if (e.target.checked) {
setLikes([
...likes,
'吃饭'
])
} else {
setLikes(likes.filter(item => item != '吃饭'))
}
}} />
<label>吃饭</label>
<input type="checkbox" checked={likes.includes('睡觉')} onChange={(e) => {
if (e.target.checked) {
setLikes([
...likes,
'睡觉'
])
} else {
setLikes(likes.filter(item => item != '睡觉'))
}
}} />
<label>睡觉</label>
<input type="checkbox" checked={likes.includes('打豆豆')} onChange={(e) => {
if (e.target.checked) {
setLikes([
...likes,
'打豆豆'
])
} else {
setLikes(likes.filter(item => item != '打豆豆'))
}
}} />
<label>打豆豆</label>
</div>
)
}
export default FormComponent
17 -《useEffect》
用来处理"副作用",实际上就是用来模拟类组件的生命周期。
一、箭头函数 + 空数组
当 useEffect 接收两个参数,第一个参数是回调函数,第二个参数是空数组时。
回调函数会在组件首次挂载完成 执行,模拟的是类组件中的 componentDidMount
生命周期。
通常可以用来发送页面初始化的网络请求:
react
useEffect(() => {
console.log('useEffect: 在组件首次挂载完成执行');
}, []);
二、箭头函数 + 非空数组
当 useEffect 接收两个参数,第一个参数是回调函数,第二个参数是非数组时,数组中可以设置多个需要"侦听"的数据。
回调函数会在组件首次挂载完成 时,并且数组中任意数据发生改变时执行。
react
useEffect(() => {
console.log('useEffect: 在组件首次挂载完成执行 + 数组中任意数据发生改变');
}, [数据一, 数据二]);
三、箭头函数
当 useEffect 接收一个参数,这个参数是一个回调函数。
回调函数会在组件首次挂载完成 时,并且组件更新时 执行,模拟的是类组件中的 componentDidMount
+ componentDidUpdate
生命周期。
react
useEffect(() => {
console.log('useEffect: 在组件首次挂载完成执行 + 组件更新');
});
四、第一个函数中返回新函数
当 useEffect 接收一个函数作为参数,同时函数中又 return 了一个新函数。
return 的这个新函数,会在组件卸载完成前执行,模拟的是类组件中的 componentWillUnmount
生命周期。
react
useEffect(() => {
return () => {
console.log('返回的新函数:在组件卸载完成前执行')
}
}, []);
18 -《组件通信》
一、父传子:props
1、父组件传值
javascript
const FatherComponent = () => {
return (
<ChildA name="张三" age={20} />
)
}
传值时,除了传递静态的字符串(例如上面的"张三"),其他数据在传递时都通过 {}
来传值。
2、子组件接收值
javascript
const ChildA = (props) => {
console.log(props);
const { name } = props;
return (
<div>
<h3>{name}</h3>
<h3>{props.age}</h3>
</div>
)
}
3、设置 props 的默认值
javascript
const ChildA = (props) => {
// 方式一:通过 ES6 的解构赋值来设置默认值
const { name = '李四' } = props;
return (
<div>
<h3>{name}</h3>
<h3>{props.age}</h3>
</div>
)
}
// 方式二:React 官方方式
ChildA.defaultProps = {
age: 18
}
4、props 的验证
javascript
import pt from 'prop-types'
const ChildA = (props) => {
const { name = '李四' } = props;
return (
<div>
<h3>{name}</h3>
<h3>{props.age}</h3>
</div>
)
}
// 设置 props 的验证
ChildA.propTypes = {
name: pt.string,
age: pt.oneOfType([pt.number, pt.string])
}
export default ChildA
常用的验证规则:
- 字符串:
pt.string
- 数字:
pt.number
- 布尔值:
pt.bool
- 数组:
pt.array
- 对象:
pt.object
- 函数:
pt.func
- 多个中的任意一个:
pt.oneOfType([pt.number, pt.string])
5、props 只读
props 中的数据,都只能使用,不能修改!!!
二、子传父:回调函数
父组件先传递一个函数给子组件,子组件中调用父组件的方法,同时传值给父组件。
1、父组件传递回调函数
javascript
const FatherComponent = () => {
const getChildData = (data) => {
console.log('接收子组件传递的参数', data);
}
return (
<div>
<ChildA name="张三" age={20} getChildData={getChildData} />
</div>
)
}
2、子组件调用回调函数并传值
javascript
const ChildA = (props) => {
return (
<div>
<button onClick={() => {
// 子组件调用父组件的方法,并将值传递给父组件
props.getChildData('你好');
}}>传值给父组件</button>
</div>
)
}
三、嵌套组件传值:context
1、创建 context 对象
我们在组件外部创建一个 context.js
文件:
javascript
import { createContext } from "react";
export default createContext(null);
2、顶层组件传值
javascript
import Context from './context.js'
const FatherComponent = () => {
return (
<Context.Provider value={{ count: 10 }}>
<ChildA name="张三" age={20} getChildData={getChildData} />
<ChildB />
</Context.Provider>
)
}
语法说明:
- 顶层组件通过
<Context.Provider>
的value
属性来传递公共数据; - 只要被
<Context.Provider>
包裹的组件,其内部子子孙孙组件都可以获取到value
中的公共数据;
3、内部组件接收值
javascript
import React, { useContext } from 'react'
import Context from './context'
const Grandson = () => {
const value = useContext(Context);
console.log(value); // { count: 10 }
return (
<div>Grandson</div>
)
}
四、兄弟组件传值:事件总线
React 中默认不支持兄弟组件传值,需要我们通过"事件总线"来实现兄弟组件传值,需要下载第三方插件。
1、下载插件
csharp
yarn add events
2、创建事件总线对象
在组件外部创建一个 event.js文件,用来创建事件总线对象:
arduino
import { EventEmitter } from 'events';
export default new EventEmitter();
3、接收数据的组件添加方法
react
import React,{useEffect } from 'react'
import event from'./event.js'
//接收数据的组件,红责给事件总线添加方法
const childB=()=>{
useEffect(()=>{
event.addListener('getData',(data)=>{
console.log('组件B接收到的A组件传递的值:',data);
})
},[])
return(
<div></div>
)
}
4、传递数据的组件调用方法
vbnet
import event from './event";
const childA =()=>{
return(
<button onclick={()=>{
//调用 event 身上的方法,并传值
event.emit('getData','hello');
}}>传值给B</button>
)
}
五、总结
- 父传子:props
- 子传父:回调函数
- 兄弟组件:事件总线、状态提升(兄弟之间通过父组件来传值)
- 嵌套组件:context