这篇文章上次修改于 215 天前,可能其部分内容已经发生变化,如有疑问可询问作者。

React 是一个用于构建用户界面的库。React 不是一个框架——它的应用甚至不局限于 Web 开发,它可以与其他库一起使用以渲染到特定环境。例如,React Native 可用于构建移动应用程序;React 360 可用于构建虚拟现实应用程序…

React

1.搭建开发环境

create-react-app快速搭建开发环境

1
npx create-react-app react-basic(项目名)

node_modules:存放项目所依赖的一些第三方包文件

public:静态资源文件夹

​ - favicon.ico:导航图标

​ - index.html:项目首页的html模版

​ - logo192.png&logo512.png:两个logo图片

​ - manifest.json:应用加壳配置文件,在手机上描述我们应用程序的json文件

​ - robots.txt:爬虫协议文件

src:源码文件夹

​ - App.css:App组件的样式

​ - App.js:App组件

​ - App.test.js:自动化测试文件,用于给App做测试

​ - index.css:全局样式

​ - index.js:入口文件,所用组件都会通过index.js载入

​ - logo.svg:react的一个标志文件,sug文件是纯粹的XML,保证了在放大缩小时图像不失真

​ - reportWebVitals.js:导入了一个web-vitals的第三方库,用来检查应用程序的性能

​ - setupTests.js:从Jest-dom导入匹配器,在进行测试时使用

其它文件

​ .gitignore:git的一个文件,在这里可以配置不想提交到版本库里的文件

​ package-lock.json:项目安装包的版本号

​ package.json:存放react的一些依赖和指令

​ README.md:项目的介绍文件,运用markdown标记语言编写,在github等开源代码网站上,项目常用.md文件来进行介绍

2.JSX

JavaScript和XML缩写,表示JS代码中编写HTML模板结构

jsx需要解析工具做解析才能渲染

1
2
3
4
5
6
7
function App() {
return (
<div className="App">
this is a app
</div>
);
}

212417.png

识别js表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let count = 10;
let getname = ()=>{
return "Ha"
}
function App() {
return (
<div className="App">
this is a app
{/* 使用引号传递字符串 */}
{'test'}
{/* 识别变量 */}
{count}
{/* 函数调用 */}
{getname()}
{/* 方法调用 */}
{new Date.now()}
{/* 使用js对象 */}
<div style={{color:'red'}}>this is a test</div>
</div>
);
}

3.动态渲染列表

map循环结构,加上唯一key值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let list = [
{id:'1001',name:'vue'},
{id:'1002',name:'react'},
{id:'1003',name:'angular'}
]
function App() {
return (
<div className="App">
<ul>
/*{list.map(item => <li>{item.name}</li>)}*/
{list.map(item=><li key={item.id}>{item.name}</li>)}
</ul>
</div>
);
}

export default App;

4.条件渲染

逻辑运算符,三元表达式

1
2
3
4
5
//简单情况的条件渲染
{isLogin && <span>this is a sapn</span>}
{isLogin ? <span>this is a span</span> : <div>this is a div</div>}
//复杂情况的条件渲染
使用函数判断并返会jsx

5.事件绑定

on+事件名称 = {事件处理程序}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function App() {
const func=()=>{
console.log("test")
}
//事件参数e
const func=(e)=>{
console.log("test",e)
}
return (
<div className="App">
<div onClick={func}>click me</div>
</div>
);
}

传递参数 使用箭头函数调用

1
2
3
4
5
6
7
8
9
10
11
12
const test ="Hello React"
function App() {
const func=(test)=>{
console.log(test)
}

return (
<div className="App">
<button onClick={()=>func(test)}>click me</button>
</div>
);
}

既要传递自定义参数,又要事件对象e

1
2
3
4
5
6
7
8
9
10
11
12
13
const test ="Hello React"
function App() {
//顺序与调用参数顺序相同
const func=(test,e)=>{
console.log(test+"____"+e)
}

return (
<div className="App">
<button onClick={(e)=>func(test,e)}>click me</button>
</div>
);
}

6.组件

组件就是首字母大写的函数,内部存放了组件的逻辑和ui视图,渲染组件只需要把组件当成标签书写即可

1
2
3
4
5
6
7
8
9
10
11
12
13
function Button(){
return <button>click me</button>
}
const button = ()=>{
return <button>click me</button>
}
function App() {
return (
<div className="App">
<Button/>
</div>
);
}

可以使用自闭合标签或成对标签

7.useState

UseState是一个React Hook(函数),它允许我们向组件添加一个状态变量,从而控制影响组件的渲染结果

相当于Vue3的reactive和ref 进行响应式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import {useState} from 'react'
function App() {
const [count,setCount] = useState(0)
const func = ()=>{
setCount(count+1);
}
return (
<div className="App">
<button onClick={()=>func()}>click me</button>
</div>
);
}

export default App;

状态不可变

使用setCount函数改变Count

设置对象

1
2
3
4
5
6
7
const func = ()=>{
setCount({
...user,
name:tom
});
}

设置数组

1
2
3
4
setTodos([{
id: todos.length,
text: text
}, ...todos]);

8.小案例

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
import {useState} from 'react'
let list = [
{id:'1001',name:'vue',author:"richu"},
{id:'1002',name:'react',author:"lisi"},
{id:'1003',name:'angular',author:"zhangsan"}
]
const user = {
uid:1001,
avatar:"https://www.???.com",
uname:"richu"
}

function App() {
const [commentslist,SetCommentslist] = useState(list)
const deleteComments = (author)=>{
if (author === user.uname){
SetCommentslist(list.filter( item=> item.author !== author))
}
}
return (
<div className="App">
<div>{user.uname}</div>
<input placeholder='发表评论'></input>
<div>{commentslist.map(item=><div key={item.id}>
<span>{item.name}</span>
<button onClick={()=>deleteComments(item.author)}>删除</button>
</div>)}</div>
</div>
);
}
export default App;

动态绑定类名

1
2
3
<div className={`${name=user.name && 'active'}`}></div>

<div className={className('item',{active:type === item.type })}></div>

active为类名 为true类名显示

9.受控表单绑定

使用React组件的状态(useState)控制表单的状态,双向绑定

通过value属性绑定react事件

绑定onChange事件,通过事件参数e拿到输入框最新的值 反向修改到react状态

1
2
3
4
5
6
7
8
9
10
11
import {useState} from 'react'

function App() {
const [value,SetValue] = useState('')
return (
<div className="App">
<input value={value} onChange={(e)=>SetValue(e.target.value)} type='text'></input>
</div>
);
}
export default App;

10.React获取Dom

使用useRef钩子函数

1.使用useRef创建ref对象,并与JSX绑定

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
import {useState,useRef} from 'react'

function App() {
const [commentslist,SetCommentslist] = useState(list)
const [value,SetValue] = useState('')
const inputRef = useRef(null)
const deleteComments = (author)=>{
if (author === user.uname){
SetCommentslist(list.filter( item=> item.author !== author))
console.log(value)
}
}
const test = () => {
console.log(inputRef.current)
}
return (
<div className="App">
<div>{user.uname}</div>
<input placeholder='发表评论'></input>
<div>{commentslist.map(item=><div key={item.id}>
<span>{item.name}</span>
<button onClick={()=>deleteComments(item.author)}>删除</button>
</div>)}</div>
<input value={value} onChange={(e)=>SetValue(e.target.value)} type='text'></input>
<input ref={inputRef}></input>
<button onClick={()=>test()}>click me!</button>
</div>
);
}
export default App;

2.在DOM可用时,通过inputRef.current拿到DOM对象

1
2
3
const test  = () => {
console.log(inputRef.current)
}

3.动态获取input的value

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
import {useState,useRef} from 'react'
let list = [
{id:'1001',name:'vue',author:"richu"},
{id:'1002',name:'react',author:"lisi"},
{id:'1003',name:'angular',author:"zhangsan"}
]
const user = {
uid:1001,
avatar:"https://www.???.com",
uname:"richu"
}

function App() {
const [commentslist,SetCommentslist] = useState(list)
const [value,SetValue] = useState('')
const inputRef = useRef(null)
const [a,SetA] = useState(4);
const deleteComments = (author)=>{
if (author === user.uname){
SetCommentslist(list.filter( item=> item.author !== author))
}
}
const test = () => {
SetCommentslist([
...commentslist
,{
id:'100'+a.toString(),
name:value,
author:user.uname
}])
SetA(a+1)
SetValue('')
}
return (
<div className="App">
<div>{user.uname}</div>
<input value={value} placeholder='你怎么看' onChange={(e)=>SetValue(e.target.value)}></input>
<button onClick={()=>test()}>发布</button>
<div>{commentslist.map(item=><div key={item.id}>
<span>{item.name}</span>
<button onClick={()=>deleteComments(item.author)}>删除</button>
</div>)}</div>
</div>
);
}
export default App;

4.实现重新聚焦

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
import {useState,useRef} from 'react'
let list = [
{id:'1001',name:'vue',author:"richu"},
{id:'1002',name:'react',author:"lisi"},
{id:'1003',name:'angular',author:"zhangsan"}
]
const user = {
uid:1001,
avatar:"https://www.???.com",
uname:"richu"
}

function App() {
const [commentslist,SetCommentslist] = useState(list)
const [value,SetValue] = useState('')
const inputRef = useRef(null)
const [a,SetA] = useState(4);
const deleteComments = (author)=>{
if (author === user.uname){
SetCommentslist(list.filter( item=> item.author !== author))
/* console.log(value) */
}
}
const test = () => {
SetCommentslist([
...commentslist
,{
id:'100'+a.toString(),
name:value,
author:user.uname
}])
SetA(a+1)
SetValue('')
inputRef.current.focus()
}
return (
<div className="App">
<div>{user.uname}</div>
<input ref={inputRef} value={value} placeholder='你怎么看' onChange={(e)=>SetValue(e.target.value)}></input>
<button onClick={()=>test()}>发布</button>
<div>{commentslist.map(item=><div key={item.id}>
<span>{item.name}</span>
<button onClick={()=>deleteComments(item.author)}>删除</button>
</div>)}</div>
</div>
);
}
export default App;

11.组件通信

1.父传子

组件之间的数据传递,父传子实现

1
2
3
4
5
6
7
8
9
10
11
12
13
function Son(props){
console.log(props)
return <div>this is son</div>
}

function App(){
const name = "hahah"
return <div>
<Son name={name}/>
</div>
}

export default App;

props可传递任意的数据

  • 数字,字符串,布尔值,数组,对象,函数,JSX

props是只读对象,子组件不能修改

2.children属性

1
2
3
4
5
6
7
8
9
10
11
12
function Son(props){
console.log(props)
return <div>this is son</div>
}

function App(){
return <div>
<Son><span>this is a span</span></Son>
</div>
}

export default App;

3.子传父

父组件给子组件传递函数,子组件调用并传递参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Son({GetMSG}){
const test = "test"
return <button onClick={()=>GetMSG(test)}>click me !!!!!</button>
}

function App(){
const GetMSG = (msg)=>{
console.log(msg)
}
return <div>
<Son GetMSG={GetMSG}></Son>
</div>
}

export default App;

4.利用状态提升兄弟组件传递

1.通过子传父传递给共同的父组件

2.通过父组件传递给兄弟组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import {useState} from 'react'

function Son({GetMSG}){
const test = "test1"
return <button onClick={()=>GetMSG(test)}>click me !!!!!</button>
}

function Son1({name}){
return <div>this is Son1,{name}</div>
}

function App(){
const [name,SetName] = useState("")
const GetMSG = (msg)=>{
SetName(msg)
}
return <div>
<Son GetMSG={GetMSG}></Son>
<Son1 name={name}></Son1>
</div>
}

export default App;

5.使用context机制实现跨层级组件通信

使用createContext方法创建一个上下文对象

在顶层组件App中通过ctx.provider提供数据

在底层组件使用useContext钩子函数获取消费数据

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 {createContext, useContext} from 'react'

//创建
const MsgContext = createContext()

function Son(){
return <div><Son1/></div>
}

function Son1(){
const test = useContext(MsgContext)
return <div>this is Son1,{test}</div>
}

function App(){
const msg = "test"
return <div>
<MsgContext.Provider value={msg}>
<Son></Son>
</MsgContext.Provider>
</div>
}

export default App;

12.useEffect

useEffect(()=>{},[])

参数已是函数,在函数内放置要执行的操作

参数2是数组,可选参,在数组里面放依赖项,不同依赖项会影响第一个参数函数的执行,当数组为空时,副作用函数在组件渲染完毕后执行一次

依赖项不同会有不同的执行表现

依赖项 副作用函数执行时机
没有依赖项 组件初始渲染,组件更新时执行
空数组依赖 只在初始渲染时执行
添加特定依赖 组件初始渲染,特性依赖项变化时执行

由渲染本身引起的对接组件外部的操作,副作用操作。比如在useEffect开启了定时器,在下载时清理

清除副作用函数,在组件卸载时会自动执行

1
2
3
4
5
6
7
8
9
useEffect(()=>{
const timer = setInterval(()=>{
console.log("test")
},1000)
return ()=>{
//清除副作用
clearInterval(timer)
}
},[])

13.自定义hook

use打头的函数,可以实现逻辑的封装和复用

1.声明一个use打头的函数

2.在函数体内封装可复用的逻辑(只要是可复用的逻辑)

使用解构赋值获得use打头的函数返回的对象的值

1
2
3
4
5
6
7
8
9
10
11
12
13
function gettoggle(){
const a= 10;
const b=10;
return {
a,
b
}
}

function App(){
const {a,b} = gettoggle()
return <div></div>
}

15.ReactHooks

只能在组件中或者其他hook函数(自定义hook函数)中调用

只能在组件的顶层调用,不能嵌套在if for 等其他函数中

使用useEffect优化案例

npm i json-server

npm i axios

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
import {useState,useRef, useEffect} from 'react'
import axios from 'axios';
const user = {
uid:1001,
avatar:"https://www.???.com",
uname:"richu"
}

function App() {
const [commentslist,SetCommentslist] = useState([])
useEffect(()=>{
async function getlist(){
const res = await axios.get("http://localhost:3005/list")
SetCommentslist(res.data)
}
getlist()
},[])
const [value,SetValue] = useState('')
const inputRef = useRef(null)
const [a,SetA] = useState(4);
const deleteComments = (author)=>{
if (author === user.uname){
SetCommentslist(commentslist.filter( item=> item.author !== author))
}
}

const test = () => {
SetCommentslist([
...commentslist
,{
id:'100'+a.toString(),
name:value,
author:user.uname
}])
SetA(a+1)
SetValue('')
inputRef.current.focus()
}
return (
<div className="App">
<div>{user.uname}</div>
<input ref={inputRef} value={value} placeholder='你怎么看' onChange={(e)=>SetValue(e.target.value)}></input>
<button onClick={()=>test()}>发布</button>
<div>{commentslist.map(item=><div key={item.id}>
<span>{item.name}</span>
<button onClick={()=>deleteComments(item.author)}>删除</button>
</div>)}</div>
</div>
);
}
export default App;

使用自定义hook封装请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function useGetlist(){
const [commentslist,SetCommentslist] = useState([])
useEffect(()=>{
async function getlist(){
const res = await axios.get("http://localhost:3005/list")
SetCommentslist(res.data)
}
getlist()
},[])
return {
commentslist,
SetCommentslist
}
}

封装Item

1
2
3
4
5
6
7
8
function Item({item,deleteComments}){
return (
<div key={item.id}>
<span>{item.name}</span>
<button onClick={()=>deleteComments(item.author)}>删除</button>
</div>
)
}

16.Redux

1.Redux

类似pinia(vuex)

通过集中管理的方式管理应用的状态

定义一个reducer对象

使用createStore方法传入reducer函数 生成一个store实例对象

使用subscribe方法订阅数据的变化

dispatch方法提交action对象触发数据变化

getState获取最新的状态数据更新到视图中

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button class="mius">-</button>
<span class="text">0</span>
<button class="add">+</button>
<script src="https://unpkg.com/redux@latest/dist/redux.min.js"></script>
<script>
//定义一个reducer函数
function reducer (state = {count:0},action){
//数据不可变,基于原始状态生成一个新的状态
if (action.type === "INCREMENT"){
return {count:state.count+1}
}
if (action.type === "DECREMENT"){
return {count:state.count-1}
}
return state
}
//使用reducer函数生成store实例
const store = Redux.createStore(reducer)

//通过store实例的subscribe订阅数据变化
store.subscribe(()=>{
console.log("state+|-")
document.querySelector(".text").innerText = store.getState().count
})
//通过dispatch函数提交状态
const mbt = document.querySelector(".mius");
mbt.addEventListener('click',()=>{
store.dispatch({
type:"DECREMENT"
})
})

const abt = document.querySelector(".add");
mbt.addEventListener('click',()=>{
store.dispatch({
type:"INCREMENT"
})
})
</script>
</body>
</html>

2.环境配置

redux Toolkit react-redux

1.使用CRA快速创建React项目

npx create-react-app test

2.安装配套工具

npm i @reduxjs/toolkit react-redux

3.启动项目

npm run start

通常集中状态管理的部分会创建一个store目录

子store模块放入modules中

store中的入口文件index.js管理组合modules的所有子模块,并导出store

3.使用redux

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
#counterStore.js
import {createSlice} from '@reduxjs/toolkit'

const counterStore = createSlice({
name:'counter',
//初始化state
initialState:{
count:0
},
reducers:{
increment(state){
state.count++
},
decrement(state){
state.count--
}
}
})

//解构
const {increment,decrement} = counterStore.actions
//获取reducer
const reducer = counterStore.reducer
//导出需要的模块
export {increment,decrement}
export default reducer
1
2
3
4
5
6
7
8
9
10
11
12
#index.js
import {configureStore} from '@reduxjs/toolkit';

//导入子模块
import counterReducer from './modules/counterStore'
const store = configureStore({
reducer:{
counter:counterReducer
}
})

export default store
1
2
3
4
5
6
7
8
9
10
11
12
13
#index.js
import ReactDOM from 'react-dom/client';
import App from './App';
import store from './store';
import { Provider } from 'react-redux';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}>
<App>

</App>
</Provider>
);
1
2
3
4
5
6
7
8
9
10
11
12
13
#App.js
import {useSelector} from 'react-redux'
function App() {
//结构使用count
const {count} = useSelector(state=>state.counter)
return (
<div>
<div>{count}</div>
</div>
);
}

export default App;

修改store的数据,使用hook函数 useDispatch,它的作用是生产提交action对象的dispatch函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#App.js
import {useDispatch, useSelector} from 'react-redux'
//导入actionCreater
import {increment,decrement} from './store/modules/counterStore'
function App() {
//结构使用count
const {count} = useSelector(state=>state.counter)
const dispatch = useDispatch()
return (
<div>
<button onClick={()=>dispatch(increment())}>+</button>
<div>{count}</div>
<button onClick={()=>dispatch(decrement())}>-</button>
</div>
);
}

export default App;

4.提交action时传递参数

在调用actionCreater的时候传递参数,参数会被传递到action对象的payload属性上

1
2
3
addToNumber(state,action){
state.count = action.payload
}

5.Redux与React异步修改

异步修改

1.创建store的写法不变,配置好同步修改方法

2.单独封装一个函数,在函数内部return一个新函数,在新函数中

​ 2.1封装异步请求获取数据

​ 2.2调用同步actionCreater传入异步数据生成一个action对象,并使用dispatch提交

3.组件中dispatch的写法保持不变

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
import { createSlice} from '@reduxjs/toolkit'
import axios from 'axios'
const ChannelStore = createSlice({
name:'channel',
initialState:{
channellist:[]
},
reducers:{
setChannels(state,action){
state.channellist = action.payload
}
}
})
const {setChannels} = ChannelStore.actions
//异步请求部分
const fetChannerllist = ()=>{
return async (dispatch)=>{
const res = await axios.get('http://geek.itheima.net/v1_0/channels')
dispatch(setChannels(res.data.data.channels))
}
}

export {fetChannerllist}

const reducer = ChannelStore.reducer

export default reducer
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
import {useEffect} from 'react'
import {useDispatch, useSelector} from 'react-redux'
//导入actionCreater
import {increment,decrement,addToNumber} from './store/modules/counterStore'
import {fetChannerllist} from './store/modules/channelStore'
function App() {
//结构使用count
const {count} = useSelector(state=>state.counter)
const {channellist} = useSelector(state=>state.channel)
const dispatch = useDispatch()
useEffect(()=>{
dispatch(fetChannerllist())
},[dispatch])
return (
<div>
<button onClick={()=>dispatch(increment())}>+</button>
<div>{count}</div>
<button onClick={()=>dispatch(decrement())}>-</button>
<button onClick={()=>dispatch(addToNumber(20))}>to 20</button>
<ul>
{channellist.map(item=><li key={item.id}>{item.name}</li>)}
</ul>
</div>
);
}

export default App;

17.ReactRouter

路由,一个路径path对应一个Component

1
2
3
4
5
6
const routes = {
{
path:"/about",
componet:about
}
}

1.创建项目并安装依赖

npx create-react-app react-router-pro

npm i

npm i react-router-dom

npm run start

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
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

import {createBrowserRouter,RouterProvider} from 'react-router-dom'

const router = createBrowserRouter([
{
path:'/login',
element:<div>login</div>
},
{
path:'/article',
element:<div>article</div>
}
])

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<RouterProvider router={router}>
<App />
</RouterProvider>

</React.StrictMode>
);

reportWebVitals();

2.抽象路由模块

在page文件夹新建文件

1
2
3
4
5
const Article = ()=>{
return <div>Article</div>
}

export default Article

router文件夹定义router

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import Article from '../page/Article'
import Login from '../page/Login'
import {createBrowserRouter} from 'react-router-dom'

const router = createBrowserRouter([
{
path:'/login',
element:<Login></Login>
},
{
path:'/article',
element:<Article></Article>
}
])

export default router

导入router

1
2
3
4
5
6
7
8
9
10
11
import router from '../router'; 
import {RouterProvider} from 'react-router-dom'

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<RouterProvider router={router}>
<App />
</RouterProvider>
</React.StrictMode>
);

3.路由导航跳转

1.声明式导航

使用Link标签

1
2
3
4
5
6
7
8
9
10
import {Link} from 'react-router-dom'

const Login = ()=>{
return (<div>
Login
<Link to="/article">跳转到文章</Link>
</div>)
}

export default Login

2.编程式导航

使用useNavigate钩子

1
2
3
4
5
6
7
8
9
10
11
import {useNavigate} from 'react-router-dom'

const Article = ()=>{
const navigate = useNavigate()
return (<div>
Article
<button onClick={()=>navigate("/login")}>去到文章</button>
</div>)
}

export default Article

4.导航传参

useSearchParams

1.传递

onClick={()=>navigate(“/login?id=1000&name=tom”)}

2.接收

1
2
3
const [params] = useSearchParams()
const id = params.get('id')
const name = params.get('name')

use

1.占位

1
2
3
4
{
path:'/article/:id',
element:<Article></Article>
}

2.传递

<Link to=”/article/1000”>跳转到文章</Link>

3.接收

1
2
const params = useParams()
const id = params.id

5.嵌套路由配置

使用children属性配置路由关系

使用Outlet组件配置二级路由渲染配置

设置默认路由

去掉path,改为index:true

6.404路由配置

当浏览器输入的url的路径在整个路由配置都找不到

1
2
3
4
{
path:'*',
element:<NotFound></NotFound>
}

history模式

createBrowerRouter

hash模式

createHashRouter

18.项目练习–>记账本

1.基本配置

1.Redux状态管理 @reduxjs/toolkit react-redux

2.路由 react-router-dom

3.时间 dayjs

4.class类名处理 classnames

5.移动端组件 antd-mobile

6.请求插件axios

npm i @reduxjs/toolkit react-redux react-router-dom dayjs classnames antd-mobile axios

2.别名路径配置

craco ===>webpack

  • npm i @craco/craco
  • craco.config.js
1
2
3
4
5
6
7
8
9
const path = require('path')

module.exports = {
webpack:{
alias:{
'@':path.resolve(__dirname,'src')
}
}
}
1
2
3
4
5
6
"scripts": {
"start": "craco start",
"build": "craco build",
"test": "react-scripts test",
"eject": "react-scripts eject"
}

jsconfig.json ===>vscode

1
2
3
4
5
6
7
8
9
10
{
"compilerOptions": {
"baseUrl": "./",
"paths": {
"@/*":[
"src/*"
]
}
}
}

3.静态界面的布置

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
import { useEffect } from 'react'
import { useDispatch } from 'react-redux'
import {Outlet} from 'react-router-dom'
import { getBillList } from '@/store/modules/billStore'
import './demo.css'
import {BillOutline,
CalculatorOutline,
AddCircleOutline
} from 'antd-mobile-icons'
import {TabBar } from 'antd-mobile'
const tabs = [
{
key: '/month',
title: '月度账单',
icon: <BillOutline />,
},
{
key: '/message',
title: '记账',
icon: <CalculatorOutline />,
},
{
key: '/year',
title: '年度账单',
icon: <AddCircleOutline />,
},
]

const Layout = ()=>{
const dispatch = useDispatch()
useEffect(()=>{
dispatch(getBillList())
},[dispatch])
return <div className='layout'>
<div className='container'>
<Outlet/>
</div>
<div className="footer">
<TabBar /* onChange={value => setRouteActive(value)} */>
{tabs.map(item => (
<TabBar.Item key={item.key} icon={item.icon} title={item.title} />
))}
</TabBar>
</div>
</div>
}

export default Layout

4.安装SCSS

npm install sass -D

1
2
3
4
5
body{
div{
color: blue;
}
}

5.安装antd

npm i antd –save

6.增加表单校验规则

1
2
3
4
<Form.Item name="verifycode" rules={[{
required:true,
message:"请输入验证码"
}]}>

7.封装axios模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//axios的封装处理
import axios from "axios";

const request = axios.create({
baseURL:"",
timeout:5000
})

request.interceptors.request.use((config)=>{
return config
},(error)=>{
return Promise.reject(error)
})

request.interceptors.response.use((response)=>{
return response.data
},(error)=>{
return Promise.reject(error)
})

export {request}

8.redux封装token

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
import {createSlice} from '@reduxjs/toolkit'
import {request} from '@/utils/index'
const userStore = createSlice({
name:"user",
initialState:{
token:''
},
reducers:{
setToken(state,action){
state.token = action.payload
}
}
})

const {setToken} = userStore.actions
const fetchLogin = (loginForm)=>{
return async (dispatch)=>{
const res = await request.post('/authorizations',loginForm)
dispatch(setToken(res.data.token))
}
}
const userReducer = userStore.reducer

export {setToken,fetchLogin}
export default userReducer

9.封装与token相关的方法,存删取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function setTokens(token){
localStorage.setItem("token",token)
}

function getToken(){
return localStorage.getItem("token")
}

function removeToken(){
localStorage.removeItem("token")
}

export {
setTokens,
getToken,
removeToken
}

10.路由权限控制

1
2
3
4
5
6
7
8
9
10
11
12
13
import {getToken} from '@/utils'
import { Navigate } from 'react-router-dom'

function AuthRoute({children}){
const token = getToken()
if (token){
return <>{children}</>
}else{
return <Navigate to={'/login'} replace/>
}
}

export {AuthRoute}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import Login from "@/pages/Login";
import Layout from "@/pages/Layout";
import {AuthRoute} from '@/components/AuthRoute'
import {createBrowserRouter} from 'react-router-dom'

const router = createBrowserRouter([
{
path:"/",
element:<AuthRoute><Layout/></AuthRoute>
},
{
path:"/login",
element:<Login/>
}
])

export default router

11.normalize.css

npm i normalize.css

初始化样式

当token失效后访问后端接口会返回401状态码

12.echarts

npm install echarts

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
import * as echarts from 'echarts';
import { useEffect, useRef } from 'react';

const BarChart = ({title})=>{
const chartRef = useRef(null)
useEffect(()=>{
//获取渲染图标的dom节点
const chartDom = chartRef.current
//初始化图标实例对象
const myChart = echarts.init(chartDom);
const option = {
title:{
text: title
},
xAxis: {
type: 'category',
data: ['React','Angular','Vue']
},
yAxis: {
type: 'value'
},
series: [
{
data: [150 , 120, 200],
type: 'bar'
}
]
};
option && myChart.setOption(option);
},[])
return <div ref={chartRef} style={{width:'500px',height:'500px'}}>Home</div>
}

export default BarChart

13.统一管理API

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { request } from "@/utils";

//登录请求
function loginAPI(formData){
return request({
url:'/authorizations',
method:'POST',
data:formData
})
}

//获取用户信息
function getProfileAPT(){
return request({
url:'/user/profile',
method:'GET'
})
}

export {loginAPI,getProfileAPT}

14.富文本

npm i react-quill@2.0.0-beta.2 –legacy-peer-deps

15.使用表单回填数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const [form] = Form.useForm()
//获取传来的id参数
const [searchparam] = useSearchParams()
const [form] = Form.useForm()
//获取id
const myid = searchparam.get("id")
useEffect(()=>{
async function getAriticle(){
const res = await getAriticleByIdAPI(myid)
form.setFieldValue(res.data)
}
getAriticle()
},[myid])

<Form
labelCol={{ span: 4 }}
wrapperCol={{ span: 16 }}
// 注意:此处需要为富文本编辑表示的 content 文章内容设置默认值
initialValues={{ type: 1 , content: '' }}
onFinish={onFinish}
form={form}
>

15.打包运行

npm run build

本地运行

1
2
npm i -g serve
serve -s build

16.路由懒加载

1.lazy函数进行动态导入

2.使用react的Suspense组件包裹路由组件

1
2
3
4
5
import {lazy} from 'react'

const Home = lazy(()=>import("@/pages/Home"))
const Article = lazy(()=>import("@/pages/Article"))
const Publish = lazy(()=>import("@/pages/Publish"))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
children:[
{
index:true,
element:<Suspense fallback={"加载中"}><Home></Home></Suspense>
},
{
path:"/publish",
element: <Suspense fallback={"加载中"}><Publish></Publish></Suspense>
},
{
path:"/article",
element:<Suspense fallback={"加载中"}><Article></Article></Suspense>
}
]

17.包体积分析

npm i source-map-explorer

1
2
3
4
5
6
7
"scripts": {
"start": "craco start",
"build": "craco build",
"test": "craco test",
"eject": "react-scripts eject",
"analyze":"source-map-explorer build/static/js/*.js"
},

18.CDN配置

craco.config.js

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
const path = require('path')

module.exports = {
webpack:{
alias:{
'@':path.resolve(__dirname,'src')
},
configure: (webpackConfig) => {
// webpackConfig自动注入的webpack配置对象
// 可以在这个函数中对它进行详细的自定义配置
// 只要最后return出去就行
let cdn = {
js: [],
css: []
}
// 只有生产环境才配置
whenProd(() => {
// key:需要不参与打包的具体的包
// value: cdn文件中 挂载于全局的变量名称 为了替换之前在开发环境下
// 通过import 导入的 react / react-dom
webpackConfig.externals = {
react: 'React',
'react-dom': 'ReactDOM'
}
// 配置现成的cdn 资源数组 现在是公共为了测试
// 实际开发的时候 用公司自己花钱买的cdn服务器
cdn = {
js: [
'https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.production.min.js',
'https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.production.min.js',
],
css: []
}
})

// 都是为了将来配置 htmlWebpackPlugin插件 将来在public/index.html注入
// cdn资源数组时 准备好的一些现成的资源
const { isFound, match } = getPlugin(
webpackConfig,
pluginByName('HtmlWebpackPlugin')
)

if (isFound) {
// 找到了HtmlWebpackPlugin的插件
match.userOptions.cdn = cdn
}

return webpackConfig
}
}
}

index.html

1
2
3
4
5
6
7
<body>
<div id="root"></div>
<!-- 加载第三发包的 CDN 链接 -->
<% htmlWebpackPlugin.options.cdn.js.forEach(cdnURL => { %>
<script src="<%= cdnURL %>"></script>
<% }) %>
</body>

19.useReducer

和useState类似,用来管理相对复杂的状态数据

1.定义一个reducer函数(根据不同的action返回不同的新状态)

2.在组件中调用useReducer,并传入reducer函数和状态的初始值

3.状态发生时通过dispatch分派一个action对象

1
const [state,dispatch] = useReducer(reducer,0)
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
import {useReducer} from 'react'

function reducer(state,action){
switch(action.type){
case "INC":
return state+1
case "DEC":
return state-1
default:
return state
}
}

function App() {
const [state,dispatch] = useReducer(reducer,0)
return (
<div className="App">
this is app ---{state}
<button onClick={()=>dispatch({type:"INC"})}>++</button>
<button onClick={()=>dispatch({type:"DEC"})}>--</button>
</div>
);
}

export default App;

20useMemo

只有在Count1发生变化才执行函数,减少重复的不必要的计算

缓存的是计算的结果

使用:消耗非常大的计算

1
2
3
4
const [count1,setCount1] = useState(5)
useMemo(()=>{
return fib(count1)
},[count1])

21.React.memo

经过memo函数包裹生成的缓存组件只有在props发生变化的时候才会重新渲染

默认渲染机制,父组件渲染子组件跟着一起渲染

1
2
3
const memoComponent = memo(function son(){
return <div>son</div>
})

props 传递一个普通类型 prop改变时组件重新渲染

props 传递一个引用类型 prop改变的时候组件比较引用是否相等,再重新渲染

保证引用稳定 –》使用useMemo 组件渲染的过程中缓存一个值

22.useCallback

在组件多次重新渲染时缓存函数

1
2
3
const handler = useCallback(()=>{
console.log("123")
},[])

23.React.forwardRef

ref暴露DOM节点给父组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const Son = forwardRef((prop,ref)=>{
return <input type="text" ref={ref}/>
})

function App(){
const SonRef = useRef(null)
const showREf = ()=>{
//console.log(SonRef)
SonRef.current.focus()
}
return (
<div className="App">
<Son ref={SonRef}/>
<button onClick={showREf}>focus</button>
</div>
)
}

export default App

24.useImperativeHandle

向父组件暴露子组件的方法

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
import { useRef,forwardRef, useImperativeHandle } from "react"

/* function Son(){
return <input type="text"/>
} */

/* const Son = forwardRef((prop,ref)=>{
return <input type="text" ref={ref}/>
})

function App(){
const SonRef = useRef(null)
const showREf = ()=>{
SonRef.current.focus()
}
return (
<div className="App">
<Son ref={SonRef}/>
<button onClick={showREf}>focus</button>
</div>
)
}

export default App */

const Son = forwardRef((prop,ref)=>{
const inputRef = useRef(null)
const inputfocus = ()=>{
inputRef.current.focus()
}
useImperativeHandle(ref,()=>{
return {
inputfocus
}
})
return <input type="text" ref={inputRef}/>
})

function App(){
const SonRef = useRef(null)
const handlerclick = ()=>{
SonRef.current.inputfocus()
}
return (
<div className="App">
<Son ref={SonRef}/>
<button onClick={handlerclick}>focus</button>
</div>
)
}

export default App

25类组件API

1.定义类属性state定义状态数据

2.通过setState方法来修改状态数据

3.通过reducer来写render来写UI模版

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
import { Component } from "react";

class Counter extends Component{
state = {
count : 0
}
setCount = ()=>{
this.setState({
count:this.state.count + 1
})
}

render(){
return <button onClick={this.setCount}>{this.state.count}</button>
}
}

function App () {
return (
<>
<Counter/>
</>
)
}

export default App

26.类组件的生命周期

componentDidMount:组件挂载完毕自动执行 异步数据获取

componentDidUpdate:组件更新

componentWillUnmount:组件卸载时自动执行 清理副作用

27.类组件中父子类传递信息

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
import {Component} from 'react'

class Son extends Component{
state={
msg : "son"
}
render(){
//使用属性 使用this.props.属性名
return <div>
son --{this.props.msg}
<button onClick={this.props.onGet(this.state.msg)}>传递</button>
</div>
}
}

class Parent extends Component{
state = {
msg : "test"
}
onGet = (Sonmsg)=>{
console.log(Sonmsg)
}
render() {
return (<div>father
<Son msg={this.state.msg} onGet={this.onGet}/>
</div> )
}
}

function App(){
return (
<div>
<Parent/>
</div>
)
}

export default App

27极简的状态管理工具zustand

1.zustand的基本使用

1.创建store 状态数据操作方法

2.component消费数据和方法

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 {create} from 'zustand'

const useStore = create((set)=>{
return {
count:1,
inc:()=>{
//需要用到老数据传递函数
set((state)=>({count:state.count + 1}))
//当不需要用到老对象时可以直接传递参数
set({count:100})
}
}
})

function App(){
const {count,inc} = useStore()
return (
<div>
<button onClick={inc}>{count}</button>
</div>
)
}

export default App

2.zustand异步支持

1
2
3
4
5
6
//异步函数逻辑
fetchList:async ()=>{
const res = await fetch("http://geek.itheima.net/v1_0/channels")
const jsonRes = await res.json()
set({channelList:jsonRes.data.channels})
}

使用

1
2
3
4
5
6
7
8
9
10
11
12
function App(){
const {count,inc,fetchList,channelList} = useStore()
useEffect(()=>{
fetchList()
},[fetchList])
return (
<div>
<button onClick={inc}>{count}</button>
<ul>{channelList.map((item)=><li key={item.id}>{item.name}</li>)}</ul>
</div>
)
}

3.zustand切片模式

当单个store比较大时,可以采用切片模式进行拆分

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
import { useEffect } from 'react'
import {create} from 'zustand'

const countStore = (set)=>{
return {
count:1,
inc:()=>{
//需要用到老数据传递函数
set((state)=>({count:state.count + 1}))
//当不需要用到老对象时可以直接传递参数
},
}
}

const channelStore = (set)=>{
return {
channelList:[],
fetchList: async ()=>{
const res = await fetch("http://geek.itheima.net/v1_0/channels")
const jsonRes = await res.json()
set({channelList:jsonRes.data.channels})
}
}
}

const useStore = create((...a)=>{//...a固定写法
return {
...countStore(...a),
...channelStore(...a)
}
})

function App(){
const {count,inc,fetchList,channelList} = useStore()
useEffect(()=>{
fetchList()
},[fetchList])
return (
<div>
<button onClick={inc}>{count}</button>
<ul>{channelList.map((item)=><li key={item.id}>{item.name}</li>)}</ul>
</div>
)
}

export default App

28.React+TypeScript

1.配置安装

npm create vite@latest react-ts-pro – –template react-ts

npm i

2.useState自动推导

1
2
3
4
5
const [value,toggle] = useState(false)
const change = ()=>{
toggle(100)
}
//类型“100”的参数不能赋给类型“SetStateAction<boolean>”的参数

3.useState泛型参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
type Person{
name:String,
age:Number
}

function App() {
const [user,Setuser] = useState<Person>()
return (
<>
<div>
this is app
</div>
</>
)
}

限制useState函数的参数为Person | ()=>Person

限制setuser函数的参数必须满足类型为Person | ()=>User | undefined

Person状态数据具备User类型相关的类型提示

4.useState初始值为null

限制useState的参数可以是Person | null

限制setUser的参数可以是Person | null

1
const [user,Setuser] = useState<Person | null>(null)

5.props类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
type Props={
className:String
}

function Son(props:Props){
const {className} = props
console.log(className)
return (<div>test</div>)
}

function App() {
return (
<>
<div>
this is app
<Son className={"test"}/>
</div>
</>
)
}

6.children

children是特殊的props,支持不同的类型数据的传入,需要通过一个内置的ReactNode类型来注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
type Props={
className:String
children:React.ReactNode
}

function Son(props:Props){
const {className,children} = props
console.log(className)
return (<div>{children}</div>)
}

function App() {
return (
<>
<div>
this is app
<Son className={"test"}>this is son</Son>
</div>
</>
)
}

7.为事件prop添加类型

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
import { useState } from "react"

/* type Person{
name:String,
age:Number
} */

type Props={
onGetMsg:(msg:String)=>void
}

function Son(props:Props){
const {onGetMsg} = props
const msg = "hello"
return <button onClick={()=>onGetMsg(msg)}>click me!!!</button>
}

function App() {
const onGetMsg = (msg:String)=>{
console.log(msg)
}
return (
<>
<div>
this is app
<Son onGetMsg={onGetMsg}/>
</div>
</>
)
}

export default App

8.useRef

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { useEffect, useRef, useState } from "react"

function App() {
const domRef = useRef<HTMLInputElement>(null)
useEffect(()=>{
//可选链
domRef.current?.focus()
},[domRef])
return (
<>
<div>
this is app
<input ref={domRef} placeholder="input"></input>
</div>
</>
)
}

export default App

当成稳定引用的存储器使用

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
import { useEffect, useRef, useState } from "react"

function App() {
const domRef = useRef<HTMLInputElement>(null)
const timeRef = useRef<number | undefined>(undefined)
useEffect(()=>{
//可选链
domRef.current?.focus()
timeRef.current= setInterval(() => {
console.log("1");
}, 1000);

return ()=>clearTimeout(timeRef.current)
},[domRef])
return (
<>
<div>
this is app
<input ref={domRef} placeholder="input"></input>
</div>
</>
)
}

export default App

9.路径别名配置

让vite做路径解析

让VScode做智能提示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//vite.config.ts

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import path from 'path'

// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
resolve:{
alias:{
'@':path.resolve(__dirname,'./src')
}
}
})

npm i @types/node -D

vscode智能提示

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
//tsconfig.app.json
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
"baseUrl": ".",
"paths": {
"@/*":[
"src/*"
]
},
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",

/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src"]
}

API模块封装

通用泛型

1
2
3
4
type ResType<T> = {
message:String,
data:T
}

具体泛型

1
2
3
4
5
6
7
8
type User = {
id:number,
name:String
}

type UserList = {
users:User[]
}

使用泛型

1
2
3
4
5
function fetchUserList(){
return http.request<ResType<UserList>>({
url:"/channels"
})
}

反文件

1
2
3
4
5
6
7
8
9
10
11
12
13
export type UserList = {
users:User[]
}

export type ResType<T> = {
message:String,
data:T
}

type User = {
id:number,
name:String
}
1
2
3
4
5
6
7
8
9
10
11
import {http} from '@/utils'
import type {ResType,UserList} from '@/apis/type/user'


function fetchUserList(){
return http.request<ResType<UserList>>({
url:"/channels"
})
}

export {fetchUserList}

使用hook优化代码,将代码逻辑抽离

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import { useEffect, useState } from 'react';
import type {User} from '@/apis/type/user'
import {fetchUserList} from '@/apis/user'

const useTab = ()=>{
const [userList,setUserList] = useState<User[]>([])
useEffect(()=>{
const getUsers = async ()=>{
try{
const res = await fetchUserList()
setUserList(res.data.data.channels)
}catch(error){
throw new Error("error")
}
}
getUsers()
},[])
return {
userList
}
}

export {useTab}