React
laoqin
# React
# 什么是 JSX
JSX
- jsx 是 javscript + xml, 是一种 js 的扩展语法
- jsx 是一种语法糖,最终会经过 babel 的转义
<h1>hello world</h1>会被转义为React.createElement("h1", null, 'hello world')- jsx 也是对象, 可以作为方法的参数 和返回值
# 什么是 react 元素
React 元素
- React 元素其实就是普通的 js 对象, 它里边有很多的属性, 描述元素的样子
- ReactDOM.render 可以保证浏览器界面的显示 和 React 元素保持一致
- 原生 dom 元素被 React.createElement() 处理成对象的形式
// 比如这样的标签
;<h1>Helllo</h1>
// 会被createElement()方法包装成对象
let reactObj = React.createElement('h1', null, 'Hello')
// 打印会得到下边的信息
console.log(reactObj)
1
2
3
4
5
6
2
3
4
5
6

# 如何更新 Dom 元素
- react 元素本身是不可变的, 当 react 元素被创建之后,更新界面的唯一方式就是创建一个新的元素,然后重新渲染
- React 只会更新必要的部分, 内部通过 dom-diff 算法进行比较
# state 状态
状态
- 数据的来源有两个地方, 分别是属性 对象 和 状态对象
- 属性是父组件传递过来的
- 状态是自己内部的,改变状态唯一的方式就是 setState
- 属性和状态的变化都会影响视图的更新
# state 状态的改变
- 不能直接修改 state 的值
- 只能使用 setState 更新状态机
import React from 'react'
import ReactDom from 'react-dom'
interface Props {}
interface State {
num: number
}
// 类组件有两个泛型, 一个 约束props, 一个约束state
class Clock extends React.Component<Props, State> {
state = {
num: 0
}
handleClick = (e: React.MouseEvent) => {
// 改变原有的值
this.setState({ num: this.state.num + 1 })
}
render() {
return (
<div>
<p> {this.state.num}</p>
<button onClick={this.handleClick}>+</button>
</div>
)
}
}
ReactDom.render(<Clock />, document.getElementById('root')!)
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# state 改变的同步和异步
- 异步更新,即使数据改变了也不会立马更新视图
interface Props {}
interface State {
num: number
}
// 组件有两个泛型, 一个 约束props, 一个约束state
class Clock extends React.Component<Props, State> {
state = {
num: 0
}
// setState 的更新可能是异步的, 多个setState 可能会被合并成一个,如果只改变一部分状态,会被合并到总的状态上边
// react 对事件进行约束
handleClick = (e: React.MouseEvent) => {
// 这样连续掉3次 会进行异步合并, 并不会立马更新,而是先缓存起来
this.setState({ num: this.state.num + 1 })
this.setState({ num: this.state.num + 1 })
this.setState({ num: this.state.num + 1 })
}
render() {
return (
<div>
<p> {this.state.num}</p>
<button onClick={this.handleClick}>+</button>
</div>
)
}
}
ReactDom.render(<Clock />, document.getElementById('root')!)
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
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
- 如果想再值改变之后立马触发视图更新,可以使用函数的方式
interface Props {}
interface State {
num: number
}
// 组件有两个泛型, 一个 约束props, 一个约束state
class Clock extends React.Component<Props, State> {
state = {
num: 0
}
// setState 的更新可能是异步的, 多个setState 可能会被合并成一个
// react 对事件进行约束
handleClick = (e: React.MouseEvent) => {
// 这样连续掉3次 会立马更新
this.setState(state => ({ num: state.num + 2 })
this.setState(state => ({ num: state.num + 2 })
this.setState(state => ({ num: state.num + 2 })
}
render() {
return (
<div>
<p> {this.state.num}</p>
<button onClick={this.handleClick}>+</button>
</div>
)
}
}
ReactDom.render(<Clock />, document.getElementById('root')!)
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
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
- setTimeout 里边的代码比较特殊, 不会走批量更新, 会立刻更新
handleClick = (e: React.MouseEvent) => {
this.setState({ num: this.state.num + 1 })
console.log(this.state.num) // 0
this.setState({ num: this.state.num + 1 })
console.log(this.state.num) // 0
setTimeout(() => {
this.setState({ num: this.state.num + 1 })
console.log(this.state.num) // 2
}, 0)
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 组件创建
组件分两种: 函数式组件 和 类组件
函数式组件就是一个函数, 接收一个属性对象 并返回 react 元素
// 定义一个组件, props的类型是Props 接口定义的类型, 返回 React 元素
function Welecom(props: Props): React.ReactElement {
return <h1>hello {props.name}</h1>
}
1
2
3
4
2
3
4
- 函数式组价是如何渲染的?
- 收集 props 对象 => (props = {name:"王老吉"})
- 把属性对象传入函数,并返回 React 元素 => (Welecom(props: Props))
- 把 React 元素渲染到页面上 => ReactDOM.render
类组件 使用 class 来定义 并继承自 React.Component
// 类组件, 需要 用泛型来约束 传入的属性
class App extends React.Component<Props> {
// 约束render 函数, 返回的是react 元素
render(): React.ReactElement {
return <h1>hello, {this.props.name}</h1>
}
}
1
2
3
4
5
6
7
2
3
4
5
6
7
- 类组件是如何渲染的?
- 收集 props 对象
- 实例化类组件的实例
- 调用类组件的 render 方法, 获得返回的 react 元素
- 把返回的 react 元素 渲染到界面上
注意事项
- React 元素可以当初 Dom 标签, 也可以是用户自定义的组件
- Dom 标签首字母与小写的, 自定义组件首字母大写
- 组件要先定义再使用
- 函数组件 只有 return 一个 React 元素, 类组件需要提供 render 方法,来返回元素
# 组件传值
React 组件传值,分为 3 种基本方式
- 父向子组件传值, 父组件直接在子组件的标签上声明要传入的属性,子组件通过
props参数接收,并且props是只读的单一数据流
// 父组件中使用了子组件Child
<Child name='hello' id={this.state.id} changeNum={this.handle} />
1
2
2
// 子组件中通过props接收属性
const Child = (props: Props) => {
// 子组件改变父组件的值
const handleClick = () => props.changeNum(50)
return (
<div>
<p>child组件</p>
<p>{props.name}</p>
<p>{props.id}</p>
<button onClick={handleClick}>子的点击事件</button>
</div>
)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
- 子组件向父组件传值, 就需要在父组件提前声明一个方法传给子组件
// 父组件声明一个方法
<Child changeNum={this.handle} />
1
2
2
// 子组件通过父组件传入的方法改变父组件的值
const Child = (props: Props) => {
const handleClick = () => props.changeNum(50)
return (
<div>
<p>child组件</p>
<button onClick={handleClick}>子的点击事件</button>
</div>
)
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
- 兄弟组件传值(该方法同样适用于父子组件),使用 context
//1. 创建一个全局上下文
const ThemeContext = React.createContext()
// 2. 创建上下文组件容器
<ThemeContext.Provider value='china is the very good contry'></ThemeContext.Provider>
// 3. 获取上下文容器中的 value 值,在组件中使用
<ThemeContext.Consumer>
{value => {
return (
<div>
{value}
</div>
)
}}
</ThemeContext.Consumer>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 父向子传值
父组件直接设置属性和值,子组件通过 props 接收
父组件
import * as React from 'react'
import Child from './Child'
class App extends React.Component {
state = {
id: 10
}
render() {
return (
<div>
{/* 传入3个属性 name, id */}
<Child name='hello' id={this.state.id} />
</div>
)
}
}
export default App
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
子组件接收
import * as React from 'react'
// 定义一个接口,约束父组件传入的属性
interface Props {
name: string
id: number
}
// Props接口 约束 props 参数
const Child = (props: Props) => {
return (
<div>
<p>child组件</p>
<p>{props.name}</p>
<p>{props.id}</p>
</div>
)
}
export default Child
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 子向父传(或改变父的)值
父组件 要提前声明一个方法传给子组件, 子组件会返回一个值作为这个函数的参数
import * as React from 'react'
import Child from './Child'
class App extends React.Component {
state = {
id: 10
}
handle = (num: number) => {
this.setState({ id: num })
}
render() {
return (
<div>
{/* 传入3个属性 name, id, changeNum */}
<p>父组件渲染:{this.state.id}</p>
<Child changeNum={this.handle} />
</div>
)
}
}
export default App
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
子组件
import * as React from 'react'
// 定义一个接口,约束父组件传入的属性
interface Props {
changeNum: (num: number) => void
}
// Props接口 约束 props 参数
const Child = (props: Props) => {
// 子组件改变父组件的值
const handleClick = () => props.changeNum(50)
// 渲染
return (
<div>
<p>child组件</p>
<button onClick={handleClick}>子的点击事件</button>
</div>
)
}
export default Child
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Context
Context
Context 适用于 共享那些对于一个组件树而言是“全局”的数据
常用 Api
React.createContext(): 创建一个 Context 对象
// 只有当组件所处的树中没有匹配到 Provider 时,其 defaultValue 参数才会生效
const MyContext = React.createContext(defaultValue)
1
2
2
- Context.Provider : 作为包裹子组件的容器, 是一个高阶组件, 提供一个 value 属性,传递给组件消费
// 只有当组件所处的树中没有匹配到 Provider 时,其 defaultValue 参数才会生效
<MyContext.Provider value={/* 某个值 */}>
1
2
2
- Context.Consumer : 用在 Provider 高阶组件下 消费的组件内部, 可以订阅到 Provider 上 value 的变更
// 高阶组件, 提供一个回调函数, 必须是同一个Context 对象
<MyContext.Consumer>
{value => {
;<div>{value}</div>
}}
</MyContext.Consumer>
1
2
3
4
5
6
2
3
4
5
6
- Class.contextType : 用组件名绑定上下文组件, 内部可以直接使用
this.context获取到Provider上的值
const ThemeContext = React.createContext()
class About extends React.Component {
render() {
return <div>About:{this.context}</div>
}
}
About.contextType = ThemeContext
1
2
3
4
5
6
7
2
3
4
5
6
7
使用 Class.contextType, Context.Provider 演示
import React from 'react'
// 创建一个上下文组件对象
const ThemeContext = React.createContext()
// 创建一个 About 组件
class About extends React.Component {
render() {
// 使用 this.context 获取值
return <div>About:{this.context}</div>
}
}
// 使用 Class.contextType绑定上下文对象
About.contextType = ThemeContext
// 组件容器
class Context extends React.Component {
render() {
return (
<>
{/* ThemeContext.Provider 创建供消费的容器 */}
<ThemeContext.Provider value='dhello world ark'>
<About />
</ThemeContext.Provider>
</>
)
}
}
export default 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
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
使用 Context.Consumer, Context.Provider 演示
import React from 'react'
// 创建一个上下文组件对象
const ThemeContext = React.createContext()
//创建Home 组件
class Home extends React.Component {
render() {
return (
<div>
{/* 使用ThemeContext.Consumer 获取 value的值 */}
<ThemeContext.Consumer>
{value => {
return <div>Home {value}</div>
}}
</ThemeContext.Consumer>
</div>
)
}
}
// ==========不需要使用 Class.contextType绑定上下文对象================//
// 组件容器
class Context extends React.Component {
render() {
return (
<>
{/* ThemeContext.Provider 创建供消费的容器 */}
<ThemeContext.Provider value='dhello world ark'>
<Home />
</ThemeContext.Provider>
</>
)
}
}
export default 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
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
# 组件声明周期
挂载阶段
constructor(): 初始化数据操作componentWillMount(): 组件挂载前, 不能拿到真实 Domrender()页面开始渲染componentDidMount(): Dom 挂载完成, 可以通过 ref 获取到真实 Dom
更新阶段
shouldComponentUpdate(nextProps, nextState): 组件是否需要更新, 仅作为性能优化的方式而存在,返回一个布尔值UNSAFE_componentWillUpdate(nextProps, nextState): 组件更新之前render(): 更新后重新渲染更新的部分componentDidUpdate(prevProps, prevState, snapshot): 会在更新后会被立即调用。首次渲染不会执行此方法。
卸载
componentWillUnmount(): 会在组件卸载及销毁之前直接调用,可以再这里清除一些副作用
# 父子组件执行顺序
挂载阶段
初始化及挂载

更新阶段

# Hooks 使用
Hook 是一个特殊的函数,它可以让你“钩入” React 的特性, 让你能在不编写 class 组件的下, 随便使用函数式进行编程, 和声明周期
不要在循环,条件或嵌套函数中调用 Hook, 确保总是在你的 React 函数的最顶层调用他们- 只在
React函数组件中使用 Hook, 不要在普通的 JavaScript 函数中调用 Hook
# useState 使用
useState()是一个方法, 唯一的参数就是初始的state数据useState()当前 state 以及更新 state 的函数
// 结构出useState
import React, { useState } from 'react'
// 函数式组件Demo
const Demo = () => {
// es6 解构 数组
const [name, setName] = useState('老王')
const changeName = () => {
console.log(name) // 老王, 这个值一直被保存, (闭包)
setName(Math.random(0, 1) * 100) // 改变值利用解构的第二个
}
return (
<div>
<p>{name}</p>
<button onClick={changeName}>字符变随机数</button>
</div>
)
}
export default Demo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# useEffect 使用
useEffect 几乎包含了类组件中的所有生命周期钩子函数的功能, 可以让你在函数组件中执行副作用操作
- 如果只传第一个参数,并且第一次初始化:就是
componentDidMount钩子的功能
- 如果只传第一个参数,并且第一次初始化:就是
- 如果只传第一个参数,并且页面数据变化:就是
componentDidUpdate钩子的功能
- 如果只传第一个参数,并且页面数据变化:就是
- 如果第一个参数中最后 return 一个函数,那这个 retrun 的函数,会在切换路由或组件销毁前执行,这个 return 的函数里的执行就是
componentWillUnmount钩子的功能
- 如果第一个参数中最后 return 一个函数,那这个 retrun 的函数,会在切换路由或组件销毁前执行,这个 return 的函数里的执行就是
- 如果第二个参数传入一个空数组: 就
单纯只是 componentDidMount钩子的功能,每次数据变化的时候不会在传入的第一个函数类型参数里的数据
- 如果第二个参数传入一个空数组: 就
- 如果第二个参数传入的数组里传入依赖的变化的项: 就是
componentDidMount 和 componentDidUpdate两个钩子的功能
- 如果第二个参数传入的数组里传入依赖的变化的项: 就是
- 对于需要清除的依赖项,需要返回一个函数进行清除副作用,比如定时器,否则会有意想不到的 bug
- 再子组件中使用时,如果不传参数, 父组件的值变化了, 就有
componentWillReceiveProps钩子的功能,子组件会一直接收到变化
- 再子组件中使用时,如果不传参数, 父组件的值变化了, 就有
- 如果传入指定的属性,就是
shoudComponentUpdate钩子的功能,它就只在这个传入的属性值变化的时候才会执行(可以很明显的知道是哪个属性引起的子组件的变化),可以提高性能
- 如果传入指定的属性,就是
# 👉 useEffect(传入第一个参数)
- 如果只传第一个参数,并且第一次初始化:就是
componentDidMount钩子的功能, - 当数据变化时, 就是
componentDidUpdate钩子的功能
也就是说,当页面加载完和 更新完,这个 hook 都会执行,下边代码会走两次
import React, { useState, useEffect } from 'react'
const Demo = () => {
const [name, setName] = useState('老王')
const changeName = () => {
setName(Math.random(0, 1) * 100)
}
useEffect(() => {
console.log('执行useEffect')
})
return (
<div>
<p>{name}</p>
<button onClick={changeName}>字符变随机数</button>
</div>
)
}
export default Demo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 👉 useEffect(return 一个函数)
retrun 这个的函数中,会在切换路由或组件销毁前执行, 这个 return 的函数里的执行就是componentWillUnmount 钩子的功能
比如清除定时器,演示
import React, { useState, useEffect } from 'react'
const Demo = () => {
const [num, setNum] = useState(1)
useEffect(() => {
let timer = setInterval(() => {
setNum(num + 1)
}, 1000)
// return 一个函数
return () => {
console.log('清除定时器')
clearInterval(timer)
}
})
return (
<div>
<p>{num}</p>
</div>
)
}
export default Demo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 👉 useEffect(componentDidMount 的功能)
- 第二个参数传入一个空数组: 就
单纯只是 componentDidMount钩子的功能
import React, { useState, useEffect } from 'react'
const Demo = () => {
const [name, setName] = useState('老王')
const changeName = () => {
setName(Math.random(0, 1) * 100)
}
// 可以看到数据更新了,也只执行了一次
useEffect(() => {
console.log('更新')
}, [name])
return (
<div>
<p>{name}</p>
<button onClick={changeName}>字符变随机数</button>
</div>
)
}
export default Demo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 👉 useEffect(实现依赖某个的值的变化,才会执行)
- 意思就是说,当页面的某个值变化了,才再次执行,这个函数, 只需要在第二个空数组里传入依赖的值就可以了
import React, { useState, useEffect } from 'react'
const Demo = () => {
const [name, setName] = useState('老王')
const [name1, setName1] = useState('hello')
const changeName = () => {
setName(Math.random(0, 1) * 100)
}
const changeName1 = () => {
setName1(Math.random(0, 1) * 100)
}
// 传入依赖变化的项
useEffect(() => {
console.log('name更新')
}, [name])
// 什么都不传传入
useEffect(() => {
console.log('name1更新')
}, [])
return (
<div>
<p>{name}</p>
<p>{name1}</p>
<button onClick={changeName}>name更新</button>
<button onClick={changeName1}>name1更新</button>
</div>
)
}
export default Demo
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
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
可以看到 name1 的值变化多次, 而对应的 useEffect 执行了一次
# 👉 useEffect(componentWillReceiveProps)
import React, { useState, useEffect } from 'react'
const Child = props => {
// 传入依赖变化的响应的 shoudComponentUpdate 为 true
useEffect(() => {
console.log('接收名字:' + props.name)
}, [props.name])
return (
<div>
<h1>子组件</h1>
<p>name:{props.name}</p>
<p>mess:{props.mess}</p>
</div>
)
}
const Demo = () => {
const [name, setName] = useState('王朗')
const [message, setMess] = useState('hello,baby')
const changeName = () => {
setName(Math.random(0, 1) * 100)
}
const changeMes = () => {
setMess('world')
}
return (
<div>
<p>{name}</p>
<Child name={name} mess={message} />
<button onClick={changeName}>name更新</button>
<button onClick={changeMes}>mess更新</button>
</div>
)
}
export default Demo
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
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
可以传入依赖变化的 props 的值,来决定要不要更新,如果不传, 就会一直执行, 而 mess 更新了, 则不会引起子组件的响应

# useReducer 和 context 实现 redux 逻辑
实现模拟一个后台管理系统的侧边栏, tag 标签卡, 头部菜单信息

容器组件
import React, { useReducer, createContext } from 'react'
import Header from './views/Header'
import Side from './views/Side'
import Tag from './views/Tag'
// 创建上下文
export const Context = createContext(null)
export const defaultState = {
list: [
{
path: '/home',
title: '首页'
}
],
activedMenu: {
path: '/home',
title: '首页'
}
}
export function reducer(state, action) {
switch (action.type) {
case 'ADD_DATA':
let isHas = state.list.some(item => item.path === action.payload.path)
let newList = state.list
if (!isHas) {
newList.push(action.payload)
}
return { ...state, list: newList, activedMenu: action.payload }
default:
throw new Error()
}
}
const Demo = () => {
const [state, dispatch] = useReducer(reducer, defaultState)
return (
<Context.Provider value={{ state, dispatch }}>
<Header></Header>
<Side></Side>
<Tag />
</Context.Provider>
)
}
export default Demo
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
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
Header 组件
import React, { useContext } from 'react'
import { Context } from './../Demo'
const Header = () => {
const { state } = useContext(Context)
const { title } = state.activedMenu
return <div>header:{title}</div>
}
export default Header
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
Side 组件
import React, { useContext } from 'react'
import { Context } from './../Demo'
const list = [
{
path: '/home',
title: '首页'
},
{
path: '/about',
title: '关于'
},
{
path: '/mine',
title: '我的'
}
]
const Side = () => {
const { dispatch } = useContext(Context)
const handleClick = item => {
dispatch({
type: 'ADD_DATA',
payload: item
})
}
return (
<div>
<p>sidebar:</p>
<ul>
{list.map(item => (
<li
key={item.title}
path={item.path}
onClick={() => handleClick(item)}
>
{item.title}
</li>
))}
</ul>
</div>
)
}
export default Side
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
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
Tag 组件
import React, { useContext } from 'react'
import { Context } from './../Demo'
const Tag = () => {
const { state } = useContext(Context)
console.log(state)
return (
<div>
<p>Tags标签页</p>
{state.list.map(item => (
<span key={item.title}>{item.title}</span>
))}
</div>
)
}
export default Tag
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16