React核心内容(二)

react笔记——(2)组件通信与插槽

(注意:正常开发时会将不同组件进行多个文件分割,以下学习时将在一个文件中处理,方便学习)

1.组件通信(React DOM组件 + React组件)

1.1 DOM组件

DOM组件:指的是react支持的所有的HTML和SVG标签

1
2
// 类似 <img src="" alt="" /> 中的属性(src 与 alt),这些都不是纯HTML写法
// 这些在React中被称为Props(道具)

1.1.1 为DOM组件设置 Props

1
2
3
4
5
6
7
8
9
10
11
import image from './logo.svg'
function App() {
return (
<div>
<img src={image}
alt=""
classname="small"
/>
</div>
)
}

1.2 DOM组件的Style属性

1.2.1 直接设置style

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import image from './logo.svg'
function App() {
return (
<div>
<img src={image}
alt=""
classname="small"
// 因为style 是一种键值的结构所以JSX允许通过键值对对象的形式来进行style的设置
style={{
width: '100vh',
height: 100,
backgroundColor:'grey'
}}
/>
</div>
)
}

export default App;

1.2.2 书写变量再引入(推荐)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const imgStyleObj = {
width: '100vh',
height: 100,
backgroundColor:'grey'
}

return (
<div>
<img src={image}
alt=""
classname="small"
style={imgStyleObj}
/>
</div>
)

1.3 JSX的展开语法

{…imgData}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import image from './logo.svg'
function App() {
// img 中的alt是不能提出来的
// 因为react中如果img没有alt属性,会给一个警告
const imgData = {
className:"small",
style:{
width: '100vh', //加单位就使用字符串类型
height: 100,
backgroundColor:'grey'
}
}
return (
<div>
<img
src={image}
alt=""
{...imgData}
/>
</div>
)
}

export default App;

注意: JSX的展开操作并不是ES6的展开运算符,ES6的展开运算符的功能是将当前对象的键值直接放到 {…imgData} 的位置上,它不能在一个没有容器的地方上单独使用的。

1
2
3
4
5
6
7
8
9
return (
<div>
<img
src={image}
alt=""
{...imgData} //这里的 {} 只是JSX的语法标记就算解开这个大括号也不会直接将属性放在IMG标签之中面试点
/>
</div>
)

2.React组件的Props(自定义的React组件)

2.1 有明确的键和值的属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
function Article (props){				右边采用解构的方式,更加灵活简洁			function Article ({title,content}){
return ( | return (
<div> |---> <div>
<h2>{props.title}</h2> 想用哪个属性用哪个,不必全部引入 <h2>{title}</h2>
<p>{props.content}</p> <p>{content}</p>
</div> </div>
) )
} }

export default function App() {
return(
// 希望对组件中的内容进行定制
/*
* 通常操作步骤:
* 1.请求功能所需的数据(例如文章信息)
* 2.创建 Article 组件
* 3.将文章的数据分别传递给 Article
*/
<>
<Article
title='标签1'
content='内容1'
/>
<Article
title='标签2'
content='内容2'
/>
<Article
title='标签3'
content='内容3'
/>
</>
)
}

2.2 没有明确的键和值的属性

例如:active(默认为true,没写就是false)表示呈现状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
//第二个<Article/> 没传active,所以就是false
function Article ({title,content,active}){
return (
<div>
<h2>{title}</h2>
<p>{content}</p>
//此处做三元检测
<p>状态:{ active ? '显示中' : '已隐藏'}</p>
</div>
)
}



export default function App() {

return(
// 希望对组件中的内容进行定制
/*
* 通常操作步骤:
* 1.请求功能所需的数据(例如文章信息)
* 2.创建 Article 组件
* 3.将文章的数据分别传递给 Article
*/
<>
<Article
title='标签1'
content='内容1'
active
/>
<Article
title='标签2'
content='内容2'
/>
<Article
title='标签3'
content='内容3'
active
/>
</>
)
}

2.3 在React组件中展开 Props的使用场景

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
//步骤五:输出content与active判断后的<p>内容
function Detail ({content,active}) {
return (
<>
<p>{content}</p>
<p>状态:{ active ? '显示中' : '已隐藏'}</p>
</>
)
}

// 步骤三:以解构的方式分别获取{title, detailData},然后输出title
// 步骤四:调用Props(Detail)
function Article ({title, detailData}){
return (
<div>
<h2>{title}</h2>
<Detail {...detailData}/>
</div>
)
}

export default function App() {
// 步骤一:打包设置
const articleData = {
title:'标签1',
detailData: {
content:'内容1',
active:'true'
}
}
return(
<>
//步骤二: 将acticleData以展开的方式注入Props(Article )
<Article
{...articleData}
/>
</>
)
}


3.将JSX作为Props传递(组件插槽)

React中将插槽功能称为将JSX作为Props传递给子组件,它被预定义了一个属性叫做children

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
function List1 ({children}) {
return(
<ul>
{children}
</ul>

)
}

function List2 ({children}) {
return(
<ul>
{children}
</ul>

)
}

export default function App() {
return (
<>
<List1>
<li>列表项1</li>
<li>列表项1</li>
<li>列表项1</li>
</List1>
<List2>
<li>列表项2</li>
<li>列表项2</li>
<li>列表项2</li>
</List2>
</>
)
}

3.1 向多个位置传递JSX

(注意,所有Props组件传递的数据都是单向的(只读的),不要尝试做修改)

(父组件 —> 子组件)(App —> List1)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// 传入参数可选的话,需要给他一个默认值,例如footer=<div>默认底部</div>
function List1 ({children,title,footer=<div>默认底部</div>}) {
return(
<>
<h2>{title}</h2>
<ul>
{children}
</ul>
{footer}
</>

)
}

// 将列表处理为数组(包含对象的形式),此处并未真的处理为数组结构(此处可以处理为数组结构),处理时按之前的一样即可
export default function App() {
return (
<>
<List1
title='列表1'
footer={<p>这是底部内容1</p>}
>
<li>内容1</li>
<li>内容2</li>
<li>内容3</li>
</List1>

<List1
title='列表2'
footer={<p>这是底部内容2</p>}
>
<li>内容A</li>
<li>内容B</li>
<li>内容C</li>
</List1>

<List1
title='列表3'
footer={<p>这是底部内容3</p>}
>
<li>内容U</li>
<li>内容I</li>
<li>内容O</li>
</List1>
</>
)
}

4.子组件向父组件传值

4.1 简单示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import {useState} from "react"
// 子组件
function Detail () {
const [status,setStatus] = useState(true)
function handleClick () {
// !true = false
// ! false = true
setStatus(!status)
}
return (
<div>
<button onClick={handleClick}>按钮</button>
<p style={{
// block: 显示
// none: 不显示
display: status ? 'block' : 'none'
}}>Detail的内容</p>
</div>
)
}


// 父组件
// 注意当前程序中父组件是不知道的,它是没有感知的
export default function App() {
return (
<>
<Detail

/>
</>
)
}

4.2 给子组件设置自定义属性

如果我们希望父组件接收子组件的状态的话,那我们就需要去给子组件设置一个自定义状态(自定义命名即可)。

(注意:这类属性并不像DOM属性一样可以通过键盘鼠标触发,而是通过代码来决定在某个时机触发)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import {useState} from "react"
function Detail ({ onActive }) {
const [status,setStatus] = useState(true)
function handleClick () {
setStatus(!status)
//在这里只要一调用,就会将改变后的status值传到App父组件
onActive(status)
}
return (
<div>
<button onClick={handleClick}>按钮</button>
<p style={{
// block: 显示
// none: 不显示
display: status ? 'block' : 'none'
}}>Detail的内容</p>
</div>
)
}


//
export default function App() {
// 在这里接收一下
function handleActive (status) {
console.log(status)
}
return (
<>
<Detail
//自定义 onActive属性
onActive={handleActive}
/>
</>
)
}

除了父子之外,还有同级组件之间的传值,同级组件传值最常用的办法就是借助父组件来进行中转,此处不过多演示。

5.使用Context进行多级组件传值

当需要进行多级传递时,再使用Props就比较麻烦了,这时使用React中的Hooks==》Context来进行操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
// <section></section> 通常应该包含一个标题元素(如 <h1> - <h6>),用于标识该章节的主题。
// 嵌套使用:<section> 标签可以嵌套使用,以表示更复杂的页面结构。

//实验案例:
import { useState } from "react"

export function Section({ children }) {
return (
<section className="section">
{children}
</section>
)
}

export function Heading({ level, children }) {
switch (level) {
case 1:
return <h1>{children}</h1>
case 2:
return <h2>{children}</h2>
case 3:
return <h3>{children}</h3>
case 4:
return <h4>{children}</h4>
case 5:
return <h5>{children}</h5>
case 6:
return <h6>{children}</h6>
default:
throw Error("未知的level:" + level)
}
}
export default function App() {
return (
<div>
<Section>
<Heading level={1}>主标题</Heading>
<Section>
<Heading level={2}>子标题</Heading>
<Heading level={2}>子标题</Heading>
<Heading level={2}>子标题</Heading>
<Section>
<Heading level={3}>子子标题</Heading>
<Heading level={3}>子子标题</Heading>
<Heading level={3}>子子标题</Heading>
<Section>
<Heading level={4}>子子子标题</Heading>
<Heading level={4}>子子子标题</Heading>
<Heading level={4}>子子子标题</Heading>
</Section>
</Section>
</Section>
</Section>
</div>
)
}
/*
*呈现:
*主标题
*子标题
*子标题
*子标题
*子子标题
*子子标题
*子子标题
*子子子标题
*子子子标题
*子子子标题
*/

(面试小点)

通过上面这张图,我们可以看到虽然都是 h1 标签,但是仍旧呈现出了 h1h5 的这种样式,这是因为在使用 section与标题标签(h1h5) 嵌套时会自然呈现出的状态。

所以我们经常说 HTML是做结构呈现的,CSS才是做样式处理的,所以即使使用的HTML标签默认尺寸符合我们要求,我们依旧要进行设置限制。

那么接下来使用react提供的钩子(createContext、useContext、createContext.Provider)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
import { createContext, useContext} from "react"

// Provider 是给 Context 提供值的一种方式
// 步骤2.value={level + 1} ==> 每一次父组件调用子组件Section都会使level + 1,再将值提供给Context
// Section接收的 children 内容是Heading运行后的结果
export function Section({ children }) {
const level = useContext(LevelContext)
return (
<section className="section">
<LevelContext.Provider value={level + 1}>
{children}
</LevelContext.Provider>
</section>
)
}

export function Heading({children }) {
// 步骤3.使用useContext 获取 Context的值,然后返回对应内容
// Heading 接收的children是文本
const level = useContext(LevelContext)
switch (level) {
case 1:
return <h1>{children}</h1>
case 2:
return <h2>{children}</h2>
case 3:
return <h3>{children}</h3>
case 4:
return <h4>{children}</h4>
case 5:
return <h5>{children}</h5>
case 6:
return <h6>{children}</h6>
default:
throw Error("未知的level:" + level)
}
}
// 此处LevelContext采用帕斯卡命名法
// 步骤1.先创建一个Context,并将其设为默认值0交给 ==> LevelContext(默认级别)
const LevelContext = createContext(0)
export default function App() {
return (
//最终调用过程 ==> APP => S ==> H
//输出过程 ==> H 返回结果==> S 返回结果==> APP
<div>
<Section>
<Heading >主标题</Heading>
<Section>
<Heading >子标题</Heading>
<Heading >子标题</Heading>
<Heading >子标题</Heading>
<Section>
<Heading >子子标题</Heading>
<Heading >子子标题</Heading>
<Heading >子子标题</Heading>
<Section>
<Heading >子子子标题</Heading>
<Heading >子子子标题</Heading>
<Heading >子子子标题</Heading>
</Section>
</Section>
</Section>
</Section>
</div>
)
}

React核心内容(二)
https://bote798.top/2025/04/30/react核心内容(二)/
作者
bote798
发布于
2025年4月30日
许可协议