react基础知识总结

文章目录

react笔记

react的特点

  1. 采用组件化模式、声明式编码,提高开大效率及组件复用率。
  2. 在react native 中可以使用react语法进行移动端开发。
  3. 使用虚拟DOM+优秀的diffing算法,尽量减少与真实DOM的交互

react基础知识

B站视频-react

react的引入

  1. 把依赖包放入文件中

  2. 在页面按照以下顺序进行引入

    	<!--  引入react核心库  注意:引入的顺序不能变 -->
    	<script type="text/javascript" src="../js/react.development.js"></script>
    	<!-- 引入react-dom  用于支持react操作DOM -->
    	<script type="text/javascript" src="../js/react-dom.development.js"></script>
    	<!-- 引入babel  用于将jsx转化为js -->
    	<script type="text/javascript" src="../js/babel.min.js"></script>
    
    
  3. 如果使用的jsx进行书写,则需要使用 <script type="text/babel">

jsx语法规则-005

jsx语法规则:

  1. 定义虚拟DOM时,不要写引号
  2. 标签中混入js表达式时要用{}
  3. 样式的类名指定不要用class,要用className className=“title”
  4. 内联样式,要用style={{ color: 'yellow ', fontSize: ‘20px’ }}方式写
    • 注意:例如color可以直接写,也可以写成"color":“yellow”
  5. jsx不能有多个根标签,只能有一个
  6. 标签必须闭合
  7. 标签首字母
    1. 若小写字母开头,则将标签转化为html中同名元素,若html中无该标签对应的同名元素,则报错。
    2. 若大写字母开头,react就去渲染对应的组件,组件没有定义,则报错。
<script type="text/babel">
		const myId = "aTgUIgu"
		const myData = "heLlO,rEaCt"
		// 1 创建虚拟DOM
		const VDom = (
			<div>
				<h2 className="title" id="{myId.toLowerCase()}">
					<span style={{ color: 'yellow ', fontSize: '20px' }}>{myData.toLowerCase()}</span>
				</h2>
				<input type="text" name="" id="" />
				<good>789</good>
			</div>
		)
		// 2 渲染虚拟DOM到页面
		ReactDOM.render(VDom, document.getElementById('test'))
	</script>

区分js语句和js表达式-006

一定注意区分:【js语句(代码)】与【js表达式】

  1. **表达式:**一个表达式会产生一个值,可以放在任何一个需要值的地方

​ 下面这些都是表达式

​ 1. a

​ 2. a+b

​ 3. demo(1)

​ 4. arr.map()

​ 5. function test(){}

  1. 语句(代码): 下面这些都是语句(代码)
    1. if(){}
    2. for(){}
    3. switch(){}
	// 模拟一些数据
		const data = ["angular", "react", "vue"]
		// 1. 创建虚拟DOM
		const Dom = (
			<div>
				<h1>前端js框架列表</h1>
				<ul>
					{
						data.map((item, index) => {
							return <li key={index}>{item}</li>
						})
					}
				</ul>
			</div>
		)
		// 2. 渲染虚拟DOM到页面
		ReactDOM.render(Dom, document.getElementById('test'))

react中定义组件

函数式组件
		// 1. 创建函数式组件
		function Demo() {
			console.log(this)// 此处的this是unfinded,因为babel编译后开启了严格模式
			return <h2>你好</h2>
		}
		// 2. 渲染组件到页面 注意:组件的首字母要大写
		ReactDOM.render(<Demo />, document.getElementById('test'))
		/*
			执行了ReactDOM.render(<Demo />)之后发生了什么?
				1. react解析组件标签,找到了Demo组件
				2. 发现组件时使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM,随后呈现在页面中
		*/
类式组件
	// 1. 创建类式组件
		class Demo extends React.Component {
			render() {
				// render是放在哪里的?--Demo的原型对象上,供实例使用。
				// render中的this是谁?--Demo的实例对象。<==>Demo组件实例对象
				console.log('this', this)
				return <h2>我是用类定义的组件(适用于复杂组件)的定义</h2>
			}
		}
		// 2. 渲染组件到左面上
		ReactDOM.render(<Demo />, document.getElementById('test'))

		/*
			执行了ReactDOM.render(<Demo />)之后发生了什么?
					1. react解析组件标签,找到了Demo组件
					2. 发现组件时使用函数定义的,随后new出来该类的实例,并通过该实例调用到原型上的render方法。
					3. 将render返回的虚拟DOM转为真实DOM,随后呈现在页面中。
		*/

函数式组件和类式组件的区别

https://segmentfault.com/a/1190000020861150

  1. 类式组件:
    1. 使用的时候,如果在里面定义函数的时候,不写成箭头函数的形式,则里面的this是undefined,因此在定义函数的时候当不需要this就可以使用普通函数,当需要this的时候使用箭头函数。
  2. 函数式组件:
    1. 函数式组件没有生命周期以及state,想要在函数式组件中使用state状态管理,通过const【num , setnum】=useState(num初始值)
    2. 函数式组件如果想要使用生命周期,那就需要在**useEffect**里面去使用。
    3. 获取父组件传递过来的信息只需要和正常函数一样接收就可以了,例如function sub(props){},这里面的props就包含传递过来的信息。

对标签属性进行类型限制以及默认属性

// 这是限制输入的数据的类型
// 上面的p小写,下面的p大写
Person.propTypes={
	name:PropTypes.string.isRequired, // isRequired表示必填
 sex:PropTypes.string,
 age:PropType.number,
 speak:PropType.func
}

// 指定默认标签属性
Person.defaultProps={
 sex:'男'
}

类型的限制和默认值一般都是在子组件中进行限制。

关于ref的使用

ref用来获取对应的组件。通过this可以看到当前组件中的都有哪些属性:

在这里插入图片描述

ref使用有两种方法:

  1. 方法一:在组件或者输入框中直接使用。例如:<input ref="input1" type="text" placeholder="点击按钮提示数据" />,然后在调用的时候通过this.refs.input1就可以使用了;

  2. 方法二:使用回调的形式使用。例如:<input ref={(currentNode) => { this.input1 = currentNode }} type="text" placeholder="点击按钮提示数据" />,然后再调用的时候通过this.input1就就可以使用了;

    回调ref的两种写法以及调用次数:写在ref里面的都只会在刚加载页面的时候执行一次,把这个节点挂载this指向的函数

    写法一:直接写在标签内,更新时会调用两次,第一次调用是在刚进入页面的时候,会自动调用一次,把这个节点挂载在this.input1上。
    <input ref={(currentNode) => {this.input1 = currentNode}} type="text"  />
    
    写法二:是写了一个箭头函数,在箭头函数中通过`this.input1 = currentNode`设置,然后就可以在其他函数中使用this.input了,这个函数只在加载的时候执行一次
    <input ref={this.saveInput} type="text"/>
        saveInput = (currentNode) => {
    			this.input1 = currentNode
    	}
    

关于**React.createRef()**的使用:

  • 使用方法:调用的时候通过:this.myRef.current.value获取里面的值

    myRef = React.createRef()
    <input ref={this.myRef} type="text" placeholder="点击按钮提示数据" />
     调用的时候通过:this.myRef.current.value获取里面的值
    

react中事件处理

  1. 通过 onXxx 属性指定事件处理函数(注意大小写) ,例如onClick–onBlur

    ​ 1) React 使用的是自定义(合成)事件, 而不是使用的原生 DOM 事件—为了很好的兼容性

    ​ 2) React 中的事件是通过事件委托方式处理的(委托给组件最外层的元素)—为了高效

  2. 通过 event.target 得到发生事件的 DOM 元素对象—不要过度使用ref

受控组件与非受控组件

  • 非受控组件:现用现取,例如输入框,输入后直接使用。外部状态改变只有第一次才会收到影响,之后不会收到影响

  • 受控组件:状态存储到state中,用的时候从state中获取。外部状态改变能够影响到内部组件

  • 受控组件可以不用写ref

e.preventDefault()// 阻止默认事件

onChange是可以获取到输入框输入的数据,方法是用过e.target.value来使用,获取组件信息通过e.target

  • 非受控组件: 用户输入A => input 中显示A
  • 受控组件: 用户输入A => 触发onChange事件 => handleChange 中设置 state.name = “A” => 渲染input使他的value变成A

高阶函数-函数柯里化

  • 高阶函数:如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数。

    • 若A函数,接收的参数是一个函数,那么A就可以称之为高阶函数;
    • 若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数;
    • 常见的高阶函数:promise setTimeout
  • 函数的柯里化:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式

    <input onChange={this.saveFormData('username')} type="text" name="username" />
    	// 保存表单数据到状态中
    	saveFormData = (e) => {
    		console.log('1', e) // 数据是函数传过来的username
    		return (event) => { 
    			console.log('2', e, event.target.value) //event.target.value是输入框输入的数据
    			this.setState({
    				[e]: event.target.value
    			})
    		}
    	}
    
  • 不用柯里化实现:

    <input onChange={(event) => { this.saveFormData('username', event) }} type="text" name="username" />
    		// 保存表单数据到状态中
    		saveFormData = (data, event) => {
    			console.log('1', data, event)
    			this.setState({
    				[data]: event.target.value
    			})
    		}
    

组件的生命周期

旧生命周期

在这里插入图片描述

在这里插入图片描述

生命周期的三个阶段(旧)

  1. 初始化阶段: 由 ReactDOM.render()触发—初次渲染

    1. constructor() — 构造器
    2. componentWillMount() ---- 组件将要挂载的钩子
    3. render()
    4. componentDidMount() 组件挂载完毕后的钩子,只会在最开始的时候调用一次===>常用, 一般在这个钩子做一些初始化的事。例如:开启定时器,发送网络请求、订阅消息
      1. 组件第一次渲染完成,此时dom节点已经生成,可以在这里调用ajax请求,返回数据setState后组件会重新渲染
      2. 此组件只会在调用组件的时候执行一次,数据更新这个组件不会被触发。
  2. 更新阶段: 由组件内部 this.setSate()或父组件重新 render 触发

    1. shouldComponentUpdate( nextProps,nextState ) {return true/false/null}---- 控制组件更新的阀门
      1. 主要用于性能优化(部分更新)
      2. 唯一用于控制组件重新渲染的生命周期,由于在react中,setState以后,state发生变化,组件会进入重新渲染的流程,在这里return false可以阻止组件的更新
        因为react父组件的重新渲染会导致其所有子组件的重新渲染,这个时候其实我们是不需要所有子组件都跟着重新渲染的,因此需要在子组件的该生命周期中做判断;
    2. componentWillUpdate() —组件将要更新的钩子
    3. render()
    4. componentDidUpdate(prevProps,prevState) —组件更新完毕后的钩子
  3. 卸载组件: 由 ReactDOM.unmountComponentAtNode()触发

    1. componentWillUnmount() 组件将要卸载的钩子===> 常用,一般在这个钩子中做一些收尾的事,例如:关闭定时器,取消订阅消息

​ 一般在这个钩子中做一些收尾的事,例如:关闭定时器,取消订阅消息

react脚手架

react安装步骤

  1. 解决下包太慢问题:

    1. npm切换成国内的镜像服务来使用,淘宝镜像:

      npm config set registry http://registry.npm.taobao.org/

    2. 查看当前的代理 npm get registry

  2. 全局安装:npm i -g create-react-app

  3. 切换到想创项目的目录,使用命令:create-react-app 项目名字

注意:在引入文件的时候,后缀是js和jsx的,引用的时候可以省略后缀

创建组件时:首字母大写

nrm的使用说明–拓展–可用可不用

nrm 是一个 npm 源管理器,允许你快速地在 npm源间切换。

的安装:npm install -g nrm

查看当前可选择源:nrm ls

查看当前源:nrm current

切换源:nrm use 名称

添加源:nrm add 名称 地址

删除源:nrm del 名称

测试源速度:nrm test 名称

react文件分析

react文件的引用顺序

  1. public文件中index.html文件时用来显示在页面上的,显示在里面的div标签
  2. src文件中的index.js文件时引入App.js文件,让其显示在public的index.html文件中
  3. App.js是引入各个组件,让其显示在页面中

public/index.html

<!DOCTYPE html>
<html lang="en">
<head>
	<link rel="icon" href="/react_jiao/public/favicon.ico" />
	<meta charset="UTF-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>Document</title>
</head>
<body>
	<div id="root"></div>
</body>
</html>

src/index.js

// 引入react核心库
import React from 'react'
// 引入ReactDOM
import ReactDOM from 'react-dom/client'
import App from './App'
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
	// <React.StrictMode>检查代码不合理的地方
	<React.StrictMode>
		<App />
	</React.StrictMode>
)

src/App.js

// 创建外壳组件App
import React, { Component } from 'react'
import Hello from './components/Hello'
import Welcome from './components/Welcome'
// ! 引入组件使用的时候,首字母大写
export default class App extends Component {
	render() {
		return (
			<div>
				<Hello></Hello>
				<Welcome></Welcome>
			</div>
		)
	}
}

样式的模块化–053

如果直接使用样式,不进行处理,则相同的样式名称会被覆盖,因此需要样式的模块化,有两种方法:

方法一:在央视的外面再加一层,例如使用Hello组件演示:

.hello{
	.title{
	background-color:Red
	}
}
import './index.css'
export default class hello extends Component {
	render() {
		return <h2 className='title'>hello,组件</h2>
	}
}

方法二: 把.css文件改为.module.css,引入文件时使用import 名字 from ‘地址’

import hello from './index.module.css'
export default class Hello extends Component {
	render() {
		return <h2 className={hello.title}>hello,组件</h2>
	}
}

react插件安装–054

插件名称: ES7+ React/Redux/React-Native snippets

快捷方式VS Code React快速代码插件ES7+ React/Redux/React-Native snippets

父传子 子传父

父传子:

子传父可以直接传递,例如把父组件的state里面的数据传递给子组件,则只需要在子组件中写入即可<List todo={this.state.todos}></List>

在子组件中使用是通过this.props.todos使用

子传父:

父组件通过向子组件传递函数,子组件修改函数的值即可实现子传父

	父组件
	a = (data) => {
		console.log('a', data)
	}
	<Header rr={this.a}></Header>

	子组件
	this.props.rr(e.target.value)

安装uuid–058

作用:生成一个不重复的数据

安装:npm i nanoid

引用:import { nanoid } from 'nanoid'

安装PropTypes库–061

PropTypes库:用于限制传递数据的类型

安装:npm i prop-types

引用:import PropTypes from 'prop-types'

使用的时候是用于判断传递过来的数据类型的,因此一般是在子组件中使用,从而判断父组件传递过来的数据是否符合规定类型。

app.js

<Header rr={this.a}></Header>

Header.js

import PropTypes from 'prop-types'
	// ! 对接收的props进行:类型、必要性的限制
	static propTypes = {
		rr: PropTypes.func.isRequired
	}

安装axios

安装:npm i axios

跨域问题:可以发送请求,但是数据返回时会被axios拦截。

react脚手架配置代理总结

方法一

在package.json中追加如下配置

"proxy":"http://localhost:5000"

说明:

  1. 优点:配置简单,前端请求资源时可以不加任何前缀。
  2. 缺点:不能配置多个代理。
  3. 工作方式:上述方式配置代理,当请求了3000不存在的资源时,那么该请求会转发给5000 (优先匹配前端资源)
方法二
  1. 第一步:创建代理配置文件
在src下创建配置文件:src/setupProxy.js
  1. 编写setupProxy.js(名字是固定的)配置具体代理规则:
const { createProxyMiddleware: proxy } = require('http-proxy-middleware');

module.exports = function(app) {
  app.use(
    proxy('/api1', {  //api1是需要转发的请求(所有带有/api1前缀的请求都会转发给5000)
      target: 'http://localhost:5000', //配置转发目标地址(能返回数据的服务器地址)
      changeOrigin: true, //控制服务器接收到的请求头中host字段的值
      /*
      	changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000
      	changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:3000
      	changeOrigin默认值为false,但我们一般将changeOrigin值设为true
      */
      pathRewrite: {'^/api1': ''} //去除请求前缀,保证交给后台服务器的是正常请求地址(必须配置)
    }),
    proxy('/api2', { 
      target: 'http://localhost:5001',
      changeOrigin: true,
      pathRewrite: {'^/api2': ''}
    })
  )
}

说明:

  1. 优点:可以配置多个代理,可以灵活的控制请求是否走代理。
  2. 缺点:配置繁琐,前端请求资源时必须加前缀。

注意:如果代理出错,可能是代码的问题,解决问题方法: React创建配置代理文件setupProxy.js,启动项目无法访问

兄弟传值–消息订阅与发布模式

  1. 安装npm i pubsub-js

  2. 在订阅和发布的文件中都引入import PubSub from 'pubsub-js'

  3. 传出数据使用: PubSub.publish('名字', 数据)

  4. 接收数据使用:

    	this.token = PubSub.subscribe('名字', (名字, data) => {
    			console.log(data)
    		})
    
  5. 在接收数据的时候,一般在组件挂载后componentDidMount

  6. 组件销毁后要取消订阅

    	componentWillUnmount(){
    		PubSub.unsubscribe(this.token)
    	}
    

react-router-dom路由

  1. react的一个插件库
  2. 转么用来实现一个SPA应用
  3. 基于react的项目基本都会用到此库

安装路由:npm i react-router-dom@5(学习的是5版本)

引入:import {} from 'react-router-dom'

用法一:

  1. 在index.js中引入import { BrowserRouter } from 'react-router-dom'
import React from 'react'
import ReactDOM from 'react-dom/client'
import { BrowserRouter } from 'react-router-dom'
import App from './App'
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(<BrowserRouter>
 <App />
</BrowserRouter>)
  1. 在App.js中通过使用如下代码实现
点击跳转
<Link className='list-group-item' to="/about">About</Link>
<Link className='list-group-item' to="/home">Home</Link>

显示对应页面
	<Route path="/about" component={About}></Route>
	<Route path="/home" component={Home}></Route>

路由组件与一般组件

  1. 写法不同:
    1. 一般组件:
    2. 路由组件:
  2. 存放位置不同:
    1. 一般组件:component
    2. 路由组件:pages
  3. 接收到的props不同:
    1. 一般组件:写组件标签时传递了什么,就能收到什么
    2. 路由组件:接收到三个固定的属性
      history:
      go: ƒ go(n)
      goBack: ƒ goBack()
      goForward: ƒ goForward()
      push: ƒ push(path, state)
      replace: ƒ replace(path, state)
      location:
      pathname: “/about”
      search: “”
      state: undefined
      match:
      params: {}
      path: “/about”
      url: “/about”

NavLink的使用

**NavLink和Link的区别就是:**点击对应的按钮,可以实现高亮的效果,通过activeClassName指定对应的样式

<NavLink activeClassName='demo' className='list-group-item' to="/about">About</NavLink>

封装NavList:

  • 创建一个MyNavList的组件,然后在里面把NavList封装进去,可以使用{…this.props}接收外面传递过来的属性,而且如果在外面标签写内容,也会通过this.props.children传递给组件

    父组件:
    <MyNavLink to="/about" title="About">AboutAAA</MyNavLink>
    
    子组件:
    <NavLink activeClassName='demo' className='list-group-item' {...this.props} />
    

Switch的使用

使用Switch可以使Route中如果出现相同路径,多个组件的,就会只显示第一个组件

import { Route, Switch } from 'react-router-dom'

<Switch>
	<Route path="/about" component={About}></Route>
	<Route path="/home" component={Home}></Route>
	<Route path="/home" component={Test}></Route>
</Switch>

就会只显示Home,不显示Test。

样式丢失的问题-082

如果随意写路径,选中后再刷新,样式就可能会出现丢失的情况,解决方法有三种:

方法一:使用HashRouter(不常用)

方法二:在public中的index.html引入的样式文件不写 ./ 写后面的这个: <link rel="stylesheet" href="/css/bootstrap.css">

方法三:在public中的index.html引入的样式文件不写./,使用%PUBLIC_URL%: <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />

路由的严格匹配与模糊匹配-exact–084

  1. 默认使用的是模糊匹配(简单记:【输入的路径】必须包含要【匹配的路径】,且顺序要一致)
  2. 开启严格匹配:
  3. 严格匹配不要随便开启,需要再开,有些时候开启会导致无法继续匹配二级路由

路由组件传递参数-声明式导航

传递params参数–086

向路由组件传递params参数:

<Link to={`/home/message/detail/${mapObj.id}/${mapObj.title}`}>

声明接收params参数:

<Route path="/home/message/detail/:id/:title" component={Detail} >

在组件中通过 const { id, title } = this.props.match.params 拿到数据

this.props.match.params

优点:刷新后也能保留住参数,参数直接在搜索栏中显示

缺点:只能传递字符串,并且参数过多的时候url会变得丑陋

传递search参数–087

安装querystring库:npm install qs

引入使用:import qs from 'qs'

向路由组件传递search参数:

<Link to={`/home/message/detail?id=${mapObj.id}&title=${mapObj.title}`}>{mapObj.title}</Link>

search无需声明接收

<Route path="/home/message/detail" component={Detail} ></Route>

在组件中,数据存储在this.props.location.search中,而且需要把数据利用qs进行处理。

		const { id, title } = qs.parse(search.slice(1))

slice(1)截取第一个字符删除。

qs说明:qs.stringify()和qs.parse()

let obj = { name: ‘tom’, age: 18 }

console.log(qs.stringify(obj)) //name=tom&age=18

let str = ‘car=奔驰&pirce=199’

console.log(qs.parse(str)) // 数据转化为obj形式

刷新后也能保留住参数,参数直接在搜索栏中显示

传递state参数–088

向路由组件中传递state参数:

<Link to={{ pathname: '/home/message/detail', state: { id: mapObj.id, title: mapObj.title } }}>{mapObj.title}</Link>

state无需声明接收:

	<Route path="/home/message/detail" component={Detail} ></Route>

在组件中,数据存储在this.props.location.state中:

	const { id, title } = this.props.location.state

刷新后也能保留住参数

push和replace两种模式

  • push是默认的模式,点击回退可以退回到上一个操作的页面

  • replace是替代模式,只需要在对应的跳转标签中加入replace即可,就会替换当前的页面,点击回退不能退回到被替换的页面

    <Link replace to={`/home/message/detail`}>{mapObj.title}</Link>
    

路由组件传递参数-编程式导航

例如点击按钮执行函数进行跳转,在函数体内书写下面代码,其他的和声明式导航一样:

		// 编程式导航--params
		this.props.history.replace(`/home/message/detail/${id}/${title}`)
或者this.props.history.push(`/home/message/detail/${id}/${title}`)
		// 编程式导航--search
		this.props.history.replace(`/home/message/detail?id=${id}&title=${title}`)
		// 编程式导航--state
		this.props.history.replace(`/home/message/detai`,{id:id,title:title})

编程式还可以使用如下api

this..props.go(n)
this..props.goBack() 回退
this..props.goForward() 前进

在一般组件中使用history

路由组件可以在不传递数据的情况下在组件中获取this.props,而一般组件则不可以。因此在一般组件中不能使用this.porps.history

解决方法:withRouter可以加工一般组件,让一般组件具备路由组件所特有的API

在一般组件中这样写引入import { withRouter } from 'react-router-dom',导出的时候使用export default withRouter(Header)

import React, { Component } from 'react'
import { withRouter } from 'react-router-dom'
class Header extends Component {
}
export default withRouter(Header)

BrowserRouter与HashRouter的区别

  1. 底层原理不一样:
    • BrowserRouter使用的是H5的history API,不兼容IE9及以下版本。
    • HashRouter使用的是URL的哈希值。
  2. path表现形式不一样
    • BrowserRouter的路径中没有#,例如:localhost:3000/demo/test
    • HashRouter的路径包含#,例如:localhost:3000/#/demo/test
  3. 刷新后对路由state参数的影响
    1. BrowserRouter没有任何影响,因为state保存在history对象中。
    2. HashRouter刷新后会导致路由state参数的丢失!!!
  4. 备注:HashRouter可以用于解决一些路径错误相关的问题。

antd组件库的基本使用

Ant Design

安装:npm i antd

如果使用的是按钮图标的话,使用方法:通过import { Button } from 'antd';引入,然后就可以直接使用;

如果使用icon图标的话需要安装npm install --save @ant-design/icons,然后引入对应的图标就可以使用: import { SyncOutlined, } from '@ant-design/icons'

样式的按需引入—095

https://ant.design/docs/react/use-with-create-react-app-cn

redux-状态管理的js库

redux是什么

  1. redux 是一个专门用于做状态管理的 JS 库(不是 react 插件库)。
  2. 它可以用在 react, angular, vue 等项目中, 但基本与 react 配合使用。
  3. 作用: 集中式管理 react 应用中多个组件共享的状态。

什么情况使用:

  1. 某个组件的状态,需要让其他组件可以随时拿到(共享)。
  2. 一个组件需要改变另一个组件的状态(通信)。
  3. 总体原则:能不用就不用, 如果不用比较吃力才考虑使用。

redux工作流程图

在这里插入图片描述

redux的使用

安装:npm i redux

redux的简单使用
  1. 创建redux文件,在里面创建store.js和count_reducer.js
  2. 在store.js中写上如下代码,用于调用以及传递信息。
    1. 引入redux中的createStore函数,创建一个store;
    2. createStore调用时要传入一个为其服务的reducer;
    3. 记得暴露store对象;
// 引入createStore,专门用于创建redux中最核心的store对象
import { legacy_createStore as createStore } from 'redux'
// 引入Count组件服务的reducer
import countReducer from './count_reducer'
export default createStore(countReducer)
  1. 在count_reducer.js中写入如下代码,用于处理数据:

    1. reducer的本质是一个函数,接收:preState,action,返回加工后的状态

      ;reducer有两个作用:初始化状态,加工状态;

    2. reducer被第一次调用时,是store自动触发的,

      • 传递的preState是undefined;在后续的执行中,这个是用来保存上依稀的数据的。
      • 传递的action是:{type:'@@REDUX/INIT_a.2.b.4},第一次这样传递是为了避免重复。
/*
   		1. 该文件是用于创建一个为Count组件服务的reducer,reducer的本质就是一个函数
   		2. reducer函数会接到两个参数,分别是:之前的状态(preState),动作对象(action)
*/
/* const initState = 0 // 初始化状态
export default function countReducer(preState=initState, action) { */
export default function countReducer(preState, action) {
	console.log(preState, action)
	if (preState === undefined) preState = 0
	const { type, data } = action
	// 根据type决定如何加工数据
	switch (type) {
		case 'increment'://return preState + data
		case 'decrement'://return preState - data
		default: // 初始化的时候
			return preState
	}
}
  1. 组件中通过 <h1>当前求和为:{store.getState()}</h1>中*store.getState()可以获取到count_reducer.js中的返回信息。

  2. 通过 store.dispatch({ type: "increment", data: value * 1 })通过**store.dispatch()**可以向store传递数据,从而给count_reducer.js

  3. 因为redux在数据更新的时候不会更新render,因此需要store.subscribe()进行更新:

方法一:在当前组件中添加这个	
componentDidMount() {
		// 检测redux中状态发生变化,只要变化,就会调用
		store.subscribe(() => {
			this.setState({})
		})
	}
方法二:在最外层的index.js中添加
import store from './redux/store'
	const root = ReactDOM.createRoot(document.getElementById('root'));
	root.render(<App />)
	store.subscribe(() => {
		root.render(<App />)
	})

redux完整版

新增文件:

  1. count_action.js 专门用于创建action对象。

    /**
     * 该文件专门为Count组价生成action对象
     */
    import { INCREMENT, DECREMENT } from './constant'
    /* const createIncrementAction = (data) => {
    	return {
    		type: 'increment', data
    	}
    } */
    // 使用这种方式写,返回的是一个对象,和上面的作用是一样的
    // export const createIncrementAction = data => ({ type: 'increment', data })
    export const createIncrementAction = data => ({ type: INCREMENT, data })
    export const createDecrementAction = data => ({ type: DECREMENT, data })
    
  2. constant.js 放置容易写错action中的type。

    /**
     * 该模块使用用于定义action对象中type类型的常量值
     * 目的只有一个:便于管理的同时防止程序员写错单词
     */
    export const INCREMENT = 'increment'
    export const DECREMENT = 'decrement'
    

同步异步版action

安装插件:npm install redux-thunk --save

在store.js中写入如下代码:import thunk from 'redux-thunk'

applyMiddleware

// 引入createStore,专门用于创建redux中最核心的store对象
import { legacy_createStore as createStore, applyMiddleware } from 'redux'
// 引入Count组件服务的reducer
import countReducer from './count_reducer'
// 引入redux-thunk,用于支持异步action,与applyMiddleware()配合使用
import thunk from 'redux-thunk'
export default createStore(countReducer, applyMiddleware(thunk))

然后就可以在count_action.js中使用返回值为函数的代码了。

export const createIncrementAsyncAction = (data, time) => {
	return () => {
		setTimeout(() => {
			store.dispatch(createIncrementAction(data * 1))
		}, time);
	}
}

在action中有同步和异步之分:

  • 同步action,就是指action的值为Object类型的一般对象
  • 异步action,就是指action的值为函数需要在store.js中引入redux-thunk插件
  1. 明确:延迟的动作不想交给组件自身,想交给action
  2. 何时需要异步action:想要对状态进行操作,但是具体的数据靠异步任务返回。
  3. 具体编码:
    1. nopm i redux-thunk,并配置在store中
    2. 创建action的函数不再返回一般对象,而是一个函数,该函数中写异步任务。
    3. 异步任务有结果后,分发一个同步的action去真正操作数据。
    4. 备注:异步action不是必须要写的,完全可以自己等待异步任务的结果了再去分发同步action。

react-redux的使用–104

安装组件:npm i react-redux -S

因为react-redux的结构逻辑:

在这里插入图片描述

因此我们需要创建一个Count的容器,可以创建一个文件夹用于存放不同的容器,命名为containders,然后在里面创建对应的容器组件文件夹。

引入方式:在Count容器组件中通过引入UI组件,使用react-redux让容器和UI组件产生关联

// 容器组件
// 引入Count的UI组件
import CountUI from '../../components/Count'
// 引入connect用于连接UI组件与redux
import { connect } from 'react-redux'

export default connect()(CountUI)

然后把App.jsx中的Count组件改为容器组件,在<Count中引入store>

import React, { Component } from 'react'
import Count from './containers/Count'
import store from './redux/store'
export default class App extends Component {
render() {
 return (
   <div>
     <Count store={store}></Count>
   </div>
 )
}
}

接下来就是详细的细节展示:

  1. 在容器组件中可以使用如下代码来接收通过App.jsx传递过来的store

  2. 容器组件通过export default connect(mapStateToProps, mapDispatchToProps)(CountUI)把数据暴露出去

    1. 其中mapStateToProps函数是用来传递状态的,也就相当于把state传递给UI组件,因此传递的是对象数据类型
    2. mapDispatchToProps是用来传递操作状态的,就相当把加减等传递过去,因此传递的是函数的数据类型
    // 容器组件
    // 引入Count的UI组件
    import CountUI from '../../components/Count'
    // 引入connect用于连接UI组件与redux
    import { connect } from 'react-redux'
    // import { INCREMENT } from '../../redux/constant'
    import { createIncrementAction, createDecrementAction, createIncrementAsyncAction } from '../../redux/count_action'
    // ? 在App.jsx中传入store,因此只需要在mapStateToProps函数中接收数据即可,不用再通过使用store.dispatch()进行接收
    /**
     * 1. mapStateToProps函数返回的是一个对象;并且函数名字可以随意书写
     * 2. 返回的对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件的props的value
     * ? 3. mapStateToProps用于传递状态
     */
    function mapStateToProps(state) {
    	return { count: state }
    }
    /**
     * 1. mapDispatchToProps函数返回的是一个对象;并且函数名字可以随意书写
     * 2. 返回的对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件的props的value
     * ? 3. mapDispatchToProps用于传递操作状态的方法
     */
    function mapDispatchToProps(dispatch) {
    	return {
    		jia: (data) => {
    			// 通知redux执行
    			// dispatch({ type: INCREMENT, data: data })
    			dispatch(createIncrementAction(data))
    		},
    		jian: data => dispatch(createDecrementAction(data)),
    		jiaSync: (data, time) => dispatch(createIncrementAsyncAction(data, time))
    	}
    }
    // ? a和b是随便写,但是作用是固定的
    // export default connect(a, b)(CountUI)
    /**
     * mapStateToProps是用来传递状态的
     * mapDispatchToProps是用来传递操作状态的
     * (CountUI)是用来表示和哪个UI组件进行关联的
     */
    export default connect(mapStateToProps, mapDispatchToProps)(CountUI)
    
  3. 数据传递过去给UI组件后,通过this.props.**()就可以使用了。下面截图显示的是容器组件传递过来的数据

    在这里插入图片描述

react-redux的优化–容器代码的优化

  1. 代码方面的优化:

    • 优化前:

      const mapStateToProps = (state) => {
      	return { count: state }
      }
      const mapDispatchToProps = (dispatch) => (
      	{
      		jia: (data) => {
      			dispatch(createIncrementAction(data))
      		},
      		jian: data => dispatch(createDecrementAction(data)),
      		jiaSync: (data, time) => dispatch(createIncrementAsyncAction(data, time))
      	}
      )
      export default connect(mapStateToProps, mapDispatchToProps)(CountUI)
      
    • 代码优化:

      export default connect(state => ({ count: state }),
      	(dispatch) => ({
      		jia: (data) => {
      			dispatch(createIncrementAction(data))
      		},
      		jian: data => dispatch(createDecrementAction(data)),
      		jiaSync: (data, time) => dispatch(createIncrementAsyncAction(data, time))
      		}) 
      )(CountUI)
      
    • API级别的优化—这个是react-redux优化的:

      export default connect(state => ({ count: state }),
      	// ! api级别的精简写法,这是因为react-redux处理的
      	{
      		jia: createIncrementAction,
      		jian: createDecrementAction,
      		jiaSync: createIncrementAsyncAction
      	}
      )(CountUI)
      
    • 注:

      • 容器组件中的store是靠props传进去的,而不是在容器组件中直接引入
      • mapDispatchToProps,也可以是一个对象
  2. 使用react-redux后就不需要在最外层的index.js文件夹中使用store中的store.subscribe(()=>{root.render()})去监控数据改变从而调用render

  3. 如果在App.js文件中使用 <Count store={store}></Count>方式,当容器组件很多的时候就需要在每个组件中都引入store,这样写很麻烦。优化方法如下:

    • 在最外层的index.js文件中使用Provider。这样就不用一个一个的写store了。

      import store from './redux/store'
      import { Provider } from 'react-redux'
      const root = ReactDOM.createRoot(document.getElementById('root'));
      root.render(
      	<Provider store={store}>
      		<App />
      	</Provider>
      )
      

文件优化:由于UI组件和容器组件写在两个文件里面,会增加文件的数量,因此可以把UI组件放到容器组件里面,然后把容器组件export default出来,UI组件就不需要export了,这样就会简化文件夹。

  1. 容器组件和UI组件整合一个文件
  2. 无需自己给容器组件传递store,给包裹一个即可。
  3. 使用了react-redux后也不用再自己检测redux中状态的改变了,容器组件可以自动完成这个工作。
  4. mapDispatchToProps也可以简单的写成一个对象
  5. 一个组件要和redux“打交道”要经过哪几步?
    1. 定义好UI组件—不暴露
    2. 引入connect生成一个容器组件,并暴露,写法如下:
      connect(
      state => ({key:value}), //映射状态
      {key:xxxxxAction} //映射操作状态的方法
      )(UI组件)
    3. 在UI组件中通过this.props.xxxxxxx读取和操作状态

react-redux数据共享版

  1. 为了使不同容器之间也能进行数据共享,因此就需要在store.js中引入combineReducers,使用方法:

    import { legacy_createStore as createStore, applyMiddleware, combineReducers } from 'redux'
    // 汇总
    const allReducer = combineReducers({
    	he: countReducer,
    	rens: personReducer,
    })
    export default createStore(allReducer, applyMiddleware(thunk))
    

    这样就可以在一个store中引入多个容器组件。

  2. 接下来只需要在容器组件中引入对应的数据即可,例如:

    // 容器组件 在state中引入
    export default connect(state => ({ count: state.he, renshu: state.rens.length }),
    	{
    		jia: createIncrementAction,
    		jian: createDecrementAction,
    		jiaSync: createIncrementAsyncAction
    	}
    )(Count)
    
  3. 引入后在UI组件中通过this,props.renshu就可以直接使用。

纯函数–112

  1. 纯函数:一个函数的返回结果只依赖传给函数的参数,并且执行过程中没有副作用。

    • 执行中没有副作用的意思是指和传入的参数有关,而且参数传入函数中没有被修改。
    //  非纯函数 返回值与a相关,无法预料
    const a = 1
    const foo = (b) => a + b
    foo(2) // => 3
    
    // 纯函数 返回结果只依赖于它的参数 x 和 b,foo(1, 2) 永远是 3。传入的参数是确定的,那么 foo(1, 2) 的值永远是可预料的。
    const a = 1
    const foo = (x, b) => x + b
    foo(1, 2) // => 3
    /************************************************************************/
     // 无副作用
    const a = 1
    const foo = (obj, b) => {
      return obj.x + b
    }
    const counter = { x: 1 }
    foo(counter, 2)                       // => 3
    counter.x                             // => 1
    
    // 修改一下 ,再观察(修改了外部变量,产生了副作用。)
    const a = 1
    const foo = (obj, b) => {
        // 数据在这里产生了修改,也就产生了副作用
      obj.x = 2;
      return obj.x + b
    }
    const counter = { x: 1 }
    foo(counter, 2)  // => 4
    counter.x   // => 2
    
    
  2. 必须遵守以下一些约束

    1. 不得改写参数数据
    2. 不会产生任何副作用,例如网络请求,输入和输出设备
    3. 不能调用 Date.now()或者 Math.random()等不纯的方法
  3. redux 的 reducer 函数必须是一个纯函数

关于redux插件

  1. 浏览器安装redux插件

  2. 在vscode中安装组件库:npm i redux-devtools-extension

  3. 在store.js文件中加入这个组件库的composeWithDevTools方法,使用方法如下:

    // 引入 redux-devtools-extension
    import { composeWithDevTools } from 'redux-devtools-extension'
    
    export default createStore(allReducer, composeWithDevTools(applyMiddleware(thunk)))
    

打包react项目

打包:npm run build

全局安装:npm i serve -g

启动服务器:serve build

react拓展

react的知识拓展

setState的两种写法

setState的相关知识:

  1. setState是一个异步的API,因此在执行的会先执同步的函数,执行完成后才回执行异步函数。
  2. 但是当setState出现在自定义的dom事件以及在setTimeout中时,它是同步执行的。
	写法一:
	const { count } = this.state
	// 对象式的setState
	this.setState({
		count: count + 1
		}, () => {
			// 能够显示最新的值
			console.log('count回调', this.state.count)
			})
	console.log('count', this.state.count)
写法二:
		// 函数式的setState
		this.setState((state, props) => {
			console.log('函数式', state, props)
			return { count: state.count + 1 }
		}, () => { })

总结:

  1. 对象式的setState是函数式的setState的简写方式(语法糖)
  2. 使用原则:
    1. 如果新状态不依赖于原状态 => 使用对象方式
    2. 如果新状态依赖于原状态=> 使用函数方式
    3. 如果需要在setState()执行后获取最新的状态数据, 要在第二个callback函数中读

路由懒加载

安装路由组件:npm i react-rouer-dom

  1. 在lazyLoad文件中的index.js组件通过这用方式引入对应的组件:const Home = lazy(() => import('./Home'))

  2. 引入react中的这几个函数:import React, { Component, lazy, Suspense } from 'react',其中lazy在引入组件的时候使用,Suspense是在路由跳转的使用使用:

    <Suspense fallback={<Loading />}>
    	{/* 注册路由 */}
    	<Route path="/about" component={About} />
    	<Route path="/home" component={Home} />
    </Suspense>
    

    其中**fallback={}**是用来在加载组件的时候等待的界面,等待的界面是直接加载,不能使用懒加载。

Hook

组件分为类式组件函数式组件,Hook是函数式组件中使用的。

Hook-useState

Hook使用是在函数式组件中使用,因为函数组件中this的定义是undefined,因此需要使用Hook。

  1. State Hook让函数组件也可以有state状态, 并进行状态数据的读写操作。

  2. 语法: const [xxx, setXxx] = React.useState(initValue)

  3. useState()说明:

    1. 参数: 第一次初始化指定的值在内部作缓存。
    2. 返回值: 包含2个元素的数组, 第1个为内部当前状态值, 第2个为更新状态值的函数。
  4. setXxx()的2种写法:

    1. setXxx(newValue): 参数为非函数值, 直接指定新的状态值, 内部用其覆盖原来的状态值;
    2. setXxx(value => newValue): 参数为函数, 接收原本的状态值, 返回新的状态值, 内部用其覆盖原来的状态值;

实例如下:

function Demo() {

	/* 	【a, b】中第一个数据就是传入的0,第二个数据是对数据进行处理的。
		const [a, b] = React.useState(0)
		console.log(a, b) */
	// ? 注意:React.useState(0)中的0只是一个初始的数据,可以写其他任何数据,在后面计算中会自动把count的值赋给它,
	const [count, setCount] = React.useState(0)
	const [name, setName] = React.useState("tom")
	function add() {
		console.log(this)
		console.log('点击加号')
		// setCount(count + 1) // 第一种写法
		// setCount(count => { return count + 1 }) // 第二种写法
		// 第二种写法简化版
		setCount(count => count + 1)
		setName('jack')
	}
	return (
		<div>
			<h2>当前求和为:{count}</h2>
			<h2>wode名字是:{name}</h2>
			<button onClick={add}>点我+1</button>
		</div>
	)
}
export default Demo
useEffect Hook
  1. Effect Hook 可以让你在函数组件中执行副作用操作(用于模拟类组件中的生命周期钩子)

  2. React中的副作用操作:
    发ajax请求数据获取
    设置订阅 / 启动定时器
    手动更改真实DOM

  3. 语法和说明:

    useEffect(() => { 
          // 在此可以执行任何带副作用操作
          return () => { // 在组件卸载前执行
            // 在此做一些收尾工作, 比如清除定时器/取消订阅等
          }
        }, [stateValue]) // 如果指定的是[], 回调函数只会在第一次render()后执行
    
  4. 可以把 useEffect Hook 看做如下三个函数的组合

    • componentDidMount()
    • componentDidUpdate()
    • componentWillUnmount()
  5. useEffect(()=>{},[])是可以传递两个参数的:

    1. 第一个参数是当数据发生变化的时候会触发里面的函数
    2. 第二个参数是监控填入进去的数据,如果一个数据都不填写,则监控所有的useState数据,如果填写一个或多个,则只监控填写进去的数据的变化,其他的不监控
Ref Hook
  1. Ref Hook可以在函数组件中存储/查找组件内的标签或任意其它数据
  2. 语法: const refContainer = useRef()
  3. 作用:保存标签对象,功能与React.createRef()一样

Fragment–用于替代组件最外层的div

这两种写法都可以取代最外层的div

<Fragment><Fragment>
<></>

Context–祖孙通信

  1. 在A组件的最外层(A的外层)使用如下代码:目的是引入Context。

    const MyContext = React.createContext()
    const { Provider, Consumer } = MyContext
    
  2. 然后在A中引入B组件的时候包裹如下代码:

    <Provider value={{ ...this.state }}>
    	{/* <Provider value={this.state.username}> */}
    		<B></B>
    </Provider>
    
  3. 在C组件中,如果是类组件,就通过static contextType = MyContext代码引入以及使用:

    class C extends Component {
    	static contextType = MyContext // 这句话是用来接收的
    	render() {
    		console.log(this.context)
    		return (
    			< div >
    				<h3>我是C组件</h3>
    				<h4>我从A组件获取的用户名是:{this.context}</h4>
    			</ div>
    		)
    	}
    }
    
  4. 如果是函数组件的话,需要通过<Consumer>进行使用,代码如下:

    function C() {
    	return (
    		< div >
    			<h3>我是C组件</h3>
    			<h4>我从A组件获取的用户名是:
    				<Consumer>
    					{
    						value => {
    							return `${value.username},年龄是${value.age}`
    						}
    					}
    				</Consumer>
    			</h4>
    		</ div>
    	)
    }
    

PureComponent的使用

import React, { PureComponent } from 'react'

export default class A extends PureComponent {
	render{
	return(
	)
	}
}

开发中会使用PureComponent 进行比较,这样就会当对应组件组件的数据没有更新的时候,就不会重新渲染数据没有改变的组件。#

renderProps–类似于Vue的插槽

https://blog.csdn.net/m0_56946322/article/details/117753276

当多个组件在一个组价中使用的时候这样的结构,当B需要调用A组件中的内容就会很不方便,因此使用这样的方式编写:

<A render={(data) => <C data={data}></C>}></A>
A组件: {this.props.render(内部state数据)}
C组件: 读取A组件传入的数据显示 {this.props.data} 
在父组件中这样调用两个组件
<A render={(name) => <B name={name} />} />
如果B需要A组件的数据只需要在A组件中使用{this.props.render()}即可,相当于vue的插槽技术

父组件:
render() {
		return (
			<div>
				<h3>我是RenderProps</h3>
					{/* <A><B /></A> */}
				/* 使用 */
				<A render={(name) => <B name={name} />} />
			</div>

		)
	}
}
class A extends Component {
	state = {
		name: '小小'
	}
	render() {
		console.log(this.props)
		const { name } = this.state
		return (
			<div>
				<h3>我是A组件</h3>
   /* 使用 */
				{this.props.render(name)}
			</div>
		)
	}
}
class B extends Component {
	render() {
		return (
			<div>
				<h3>我是B组件,{this.props.name}</h3>
			</div>
		)
	}
}

新的思路逻辑:

  1. 在RenderProps里面通过调用<A> <B /> </A>,一般的方法是在RenderProps里面调用A,然后再在A的里面通过使用{this.props.children}调用B,这样AB都可以显示在页面上了。

  2. 使用RenderProps的方式书写要相对简单一些:

    1. 在最外层组件中通过<A render={(data) => <B data={data} />} />其中render中的data数据是在A组件汇总传递出去的,传递方式是通过一下方式进行传递:

      class A extends Component{
      state={
      	name:'小小',
      	age:'18'
      }
      render(){
      	const {name, age} = this.state
      	return {
      	<div>
              <h3>我是A组件</h3>
              {   
              	// 传递多个属性的时候记得加 { }
              	this.props.render({name,age})
                 // 如果值传递一个属性的时候不用加 { }
                // this.props.render(name)
          	  }
          }
      	</div>
      	}
      }
      }
      
    2. 通过this.props.render({name,age})就把数据传递给了<A render={(data) => <B data={data} />} />中的data,然后通过箭头函数显示出B并把输入传入到B中,B只需要按照正常的方式接收数据即可。

    3. B接收的时候使用this.props.名字,因为传递给B的时候使用的是data,因此在接收的时候使用this.props.data。

错误边界

错误边界:用于捕获后代组件错误,渲染出备用页面;

特点:只能捕获后代组件生命周期产生的错误,不能捕获自己组件产生的错误和其他组件在合成事件、定时器中产生的错误。

  1. 当父组件的子组件在报错的时候就会触发这个函数,并且携带错误信息static getDerivedStateFromError(err){}

  2. 组件渲染出错会调用这个函数componentDidCatch(){},用于统计错误,然后反馈给服务器。

  3. 使用:

    // 生命周期函数,一旦后台组件报错,就会触发
    	// 当父组件的子组件报错的时候,会触发这个函数,并携带错误信息
    	static getDerivedStateFromError(err) {
    		console.log('err', err)
    		return { hasError: err }
    	}
    	// 组件渲染时出错会调用这个函数,用于统计错误,反馈给服务器
    	componentDidCatch() {
    		console.log('渲染错误,反馈给服务器,通知编码人员')
    	}
    
    	render() {
    		return (
    			<div>
    				<h3>父组件</h3>
    				{this.state.hasError ? <h2>当前网络不稳定,稍后再试</h2> : <Child></Child>}
    			</div>
    
    		)
    	}
    

React-router6相关知识

路由的安装

安装:npm i react-router-dom

关于Routes

router6使用Routes代替Switch,以及引入路径的改变。使用element代替component:

重定向使用:Navigate

// react-router5的写法
import { Route, Switch } from 'react-router-dom'
	<Switch> 
		<Route path="/about" component={About}></Route>
		<Route path="/home" component={Home}></Route>
		<Route path="/home" component={Test}></Route>
	</Switch>
// react-router6的写法
import { Route, Routes, Navigate } from 'react-router-dom'
	<Routes>
		<Route path="/about" element={<About />}></Route>
		<Route path="/home" element={<Home />}></Route>
		// 重定向
		<Route path="/home" element={<Navigate to="/about" />}></Route>
	</Routes>

useRoutes路由表的使用

使用useRoutes:

import { Navigate, useRoutes } from 'react-router-dom'
const ele = useRoutes(
[
	{
		path: '/about',
		element: <About />
	},
	{
		path: '/home',
		element: <Home />,
  chidlren:[
  {
  // 注意,二级路由children里面的路径不用写 /
  path:'news',
  element:<News />
}
  ]
	},
	{
		path: '/',
		element: <Navigate to='/about' />
	},
]
)

// 然后在下面直接使用{ele}就可以了
<div className="panel-body">
	{ele}
</div>

一般情况下路由表和页面不在同一个文件夹中,然后我们就可以使用如下方式:

// 当路由表和使用它的文件不在一个页面的时候
// 路由表文件
export default [
	{
		path: '/about',
		element: <About />
	},
	{
		path: '/home',
		element: <Home />
	},
	{
		path: '/',
		element: <Navigate to='/about' />
	},
]

// 在引入它的文件中
import { useRoutes } from 'react-router-dom'
//然后再下面使用,其中router是引用的上面的文件的文件名
const ele = useRouters(router)
// 然后在render中使用
{ele}

react-router6路由传参

params传参
  1. 传递参数:

    <Link to={`detail/${m.id}/${m.title}/${m.content}`}>{m.title}</Link>
    
  2. 路由设置:

    {
    	// params传参
    	path: 'detail/:id/:title/:content',
    	element: <Detail />
    }
    
  3. 接收参数:import { useParams } from 'react-router-dom'

    //? useParams是router6中用来获取params传递过来的数据的
    import { useParams } from 'react-router-dom'
    const { id, title, content } = useParams()
    
search传参
  1. 传递参数:

    <Link to={`detail?id=${m.id}&title=${m.title}&content=${m.content}`}>{m.title}</Link>
    
  2. 路由不用设置。

  3. 接收参数:import { useSearchParams } from 'react-router-dom'

    import { useSearchParams } from 'react-router-dom'
    	const [search, setSearch] = useSearchParams()
    	const id = search.get('id')
    	const title = search.get('title')
    	const content = search.get('content')
    // 还可以通过下面这个方法更改数据
    <button onClick={() => setSearch('id=008&title=哈哈&content=ccc')}>点我更新</button>
    
state传参
  1. 传递参数:

    <Link to="detail" state={{ id: m.id, title: m.title, content: m.content }}>{m.title}</Link>
    
  2. 路由不用设置。

  3. 接收参数:import { useLocation } from 'react-router-dom'

        import { useLocation } from 'react-router-dom'
    // 连续结构赋值
        const { state: { id, title, content } } = useLocation()
    

react-router6编程式导航–函数式组件

  1. 编程式导航需要用到useNavigate :import {useNavigate } from 'react-router-dom'

  2. <button onClick={() => showDetail(m)}>点击跳转</button>
    	function showDetail(m) {
    		console.log('m', m)
    		// ? 编程式导航
    		navigate('detail', {
    			replace: false,
            // 注意:这里只能写state    
    			state: {
    				id: m.id,
    				title: m.title,
    				content: m.content
    			}
    		})
    	}
    
  3. 前进后退:

    import { useNavigate } from 'react-router-dom'
    
    	<button onClick={forward}>前进</button>
    
    	const navigate = useNavigate()
    	function forward() {
    		navigate(1)
    	}
    

零碎知识

  • 单选框中的:defaultChecked只在第一次起作用

  • 要想把state的数据都传给子组件使用: <List {...this.state}></List>

  • 要想把数据都给state,使用:

	updateAppState = (updateObj) => {
		this.setState(updateObj)
	}
  • **行内样式的书写:**style={{color:‘red’}}
  • **连续解构赋值:**const { keyWordElement: { value } } = this
  • 引入样式:一般在public文件中的index.html中引入。<link rel="stylesheet" href="./css/bootstrap.css">
  • react中路由的匹配都是从最外层开始匹配
  • 路由组件可以在不传递数据的情况下在组件中获取this.props,而一般组件则不可以。因此在一般组件中不能使用this.porps.history

关于多个括号的问题:aaa()()

		var i = 1;
		function f() {
			i++;
			console.log("-------------  :  " + i)
			return F;
			function A() {
				return "I love you!"
			}
			function F() {
				console.log('3333333')
				return B;

				function B() {
					return "I love you!"
				}
			}
		}
		f();
		console.log("111111111111")
		console.log('aa', f()()())
		alert(f()()())

react的@装饰模式

https://cloud.tencent.com/developer/article/1760332

装饰器相当于可以简化书写。

  1. **正常情况下:**首先写了一个高阶组件A,在A里面把传递过来的参数组件渲染出来。然后在B组件中export default A(componentB);,这样就把B组件当成参数传递给A组件了。

    1. 如下是componentA.js一个高阶组件:

      import React, { Component } from 'react';
      
      function A(WrappedComponent) {
        // 函数接收一个组件为参数,并返回一个类组件,继承自Component
        return class componentA extends Component {
          render() {
            return (
              <div>
                <WrappedComponent />
              </div>
            );
          }
        };
      }
      export default A;
      
    2. 如下componentB.js一个组件:

      import React, { Component } from 'react';
      import A from './componentA'; // 引入高阶组件
      
      class componentB extends Component {
        render() {
          return <div>我是组件B</div>;
        }
      }
      export default A(componentB); // 直接调用A,将组件componentB作为参数传入
      
  2. 使用装饰器:要引入的上面直接使用@+函数名就可以,导出的时候只写要导出的组件就行,默认就会把导出的组件当做参数传进给@+函数。

    1. componentB.js组件中

      import React, { Component } from 'react';
      import A from './componentA'; // 引入高阶组件
      
      @A // 直接@+函数名就可以了的
      class componentB extends Component {
        render() {
          return <div>我是组件B</div>;
        }
      }
      export default componentB; // 这里直接返回componentB组件
      

关于项目中封装的类似于React.Component的使用

在项目制作过程中,我们需要对一些经常用到的公共的方法进行添加操作,这样比较麻烦,一种简单的方法就是使用一个封装的组件来代替Component导入。

例如:在Def2中添加一些方法,然后可以在Dfef1中直接使用。

注意:在Def1中使用功能的时候要引入Def2,然后就能直接使用里面的方法了。

import React, { Component } from 'react'

export default class Def2 extends Component {
	getLog=() => {
		console.log('这是Def2的方法')
	}
}
import React, { Component } from 'react'
import Def2 from './Def2'

export default class Def1 extends Def2 {
	componentDidMount(){
		this.getLog()
	}
	render() {
		return (
			<div>Def1</div>
		)
	}
}

疑问

在组件中什么时候使用箭头函数

{
	item.auditState === 1 && <Button danger onClick={handleRervert(item)}>撤销</Button>
}
{
	item.auditState === 1 && <Button danger onClick={()=>handleRervert(item)}>撤销</Button>
}

上面代码的不同点是一个使用的箭头函数,一个是直接写,区别是直接写的会在一开始就执行,然后后面就不再执行,箭头函数是点击的时候才会执行。

四种不同的调用方式

是否使用箭头函数以及调用的时候是否加()都是不一样的。

  1. 调用的时候:

    1. 普通函数是否加()的区别:

      1. 写成普通函数的形式{this.handle},不加括号则一开始不会调用,只有点击的时候才会调用。
      2. 普通函数加()则一开始进去就会调用,后面不再执行。
    2. 箭头函数是否加{ }的区别:

      1. 箭头函数加上 { } 的时候默认没有return,如果函数有返回值的话需要在{ return }才能接收返回的数据。
      2. 箭头函数不加 { } 则默认有return,当函数如果没有返回值,则加不加 { } 都一样。
      • 箭头函数加上花括号需要写return,不加花括号不需要写,默认就有return。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值