2023-03-30 22:13阅读: 433评论: 0推荐: 1

React 16&18 笔记

Date: 2023-03-30 20:36:05
视频链接:尚硅谷React教程
ps:开始学 react 了😆
练习项目地址

前置知识

三件套 HTML、CSS、JS(ES6 语法)
当然学个 TS 更好,有助于理解,对于react,你 js 有多6,react 就有多6

P1 react 简介

React 只关注视图,不关心数据

笔记基于版本 react@16,在记录过程中引入并使用高版本特性

P2 react 案例

babel.min.js \\ 用来将 jsx 翻译成 js 文件的。
react.development.js \\ 开发,react 核心库
react-dom.development.js \\ 操作 DOM,react 周边库

React 基本实现

<div id="test"></div>
<script>
const VDOM= `<h1>hello,world!</h1>`
ReactDOM.render(VDOM,document.getElementById('test'))
</script>

P3 虚拟 DOM 的两种创建方式

为什么要使用 jsx 写法呢?

方便的创建 VDOM,在 JS 里写 HTML

// 第一种创建方式如上
const VDOM = (
<h1 id="title">
<span>Hello,React</span>
</h1>
);
// 第二种方式
const VDOM = React.createElement("h1",{ id: "title" },
React.createElement("span", {}, "Hello,React")
);
// 若是多级元素,则需要嵌套着写,比较麻烦

babeljsx 翻译为第二种方式,所以 jsx 也是一种简写语法糖.

P4 虚拟DOM与真实DOM

VDOM虚拟DOMTDOM真实DOM

VDOM 是一个 Obj,但相对 TDOM 这个对象较简单,即 VDOM 较为轻量

P5 jsx 语法规则

XML 被 JSON 取代了,
JSON => Obj,可用 JSON.parse
Obj => JSON,可用 JSON.stringfy
在 localStorage 可用其存 Obj、Arr。

jsx 语法规则,🌠 以下为具体规则,其在下面实例代码中体现

  1. 定义VDOM,使用 ()
  2. 标签混入 JS 表达式,使用 {}
  3. 如果想要写类名,需要使用驼峰命名法 className
  4. 如果想要写样式 在标签上
    style={{color:'pink',fontSize:60px}}
  5. VDOM 只能有一个根标签(类似 vue2)
  6. 标签务必闭合
  7. 标签首字母小写会解析成HTML元素,大写为组件
const myId = "imID";
const myData = "HeLlo,world!";
//1.创建虚拟DOM
const VDOM = (
<div>
<h2 className="title" id={myId.toLowerCase()}>
<span style={{ color: "pink", fontSize: "60px" }}>
{myData.toLowerCase()}
</span>
</h2>
<input type="text" />
</div>
);
//2.渲染虚拟DOM到页面
ReactDOM.render(VDOM, document.getElementById("test"));

P6 jsx 小练习

js 表达式与 js 语句的区别?

js 表达式 会 return 一个 val,用 console.log() 即可查看
js 语句 则不会返回值

面试题也会问

常用方法

const arr=[1,2,3]
const result = arr.map(num => num+1) // [2,3,4]

P7 组件与模块

  • 模块 js 代码封装的功能
  • 组件 js,html,css 的集合体(有必要将 img,video 放在一起吗?可在 assets 文件夹里,建立文件夹)
  • 模块化、组件化、工程化

P8 开发者工具的安装

chrome 的插件商店安装 react,redux 即可,需FQ
reactprofiler 为性能测试工具

P9 函数式组件

//1.创建函数式组件
function ReactFunctionComponent() {
console.log(this); //此处的this是undefined,因为babel编译后开启了严格模式
return <h2>我是用函数定义的组件(适用于【简单组件】的定义)</h2>;
}
ReactDOM.render( //2.渲染组件到页面
<ReactFunctionComponent />,
document.getElementById("test")
);

执行过程

  1. React 解析标签过程中,发现首字母大写标签
  2. 调用函数组件,然后将 VDOM => TDOM

P10 复习类相关知识

看这里即可,不想复制了🤔

P11 类式组件

React 中可以写函数式组件和类式组件,用的比较多的是类式组件,
因为函数式相对类处理业务较为麻烦,Hooks 出来后,函数式组件也可使用 State 之类的,但一般稍微复杂点的组件还是用 类式组件进行书写

P12~19 React三大属性 state

对 state 的理解 P12

statevue的data() 类似,用来存储业务相关数据的

初始化 state P13

定义 state

react中的事件绑定 P14

假如你想在元素上添加一个点击事件,跟 vue 模板语法类似,使用 onClick={handleClick} 在标签上书写,然后在类中定义方法。

类中的 this 指向问题 P15

class Person {
handleClick = ()=>{
//箭头函数 this 指向父元素,即 react组件实例,可直接调用
···
}
// 第二种
handle() {
//存在 this 指向问题,class使用局部严格模式,指向 undefind
···
}
}

解决类中的 this 指向问题 P16

如果用第二种写法,存在 this 指向问题,可以在构造函数中将 handClick 指向原型对象,非实例对象

constructor() { //构造函数,用于初始化状态,执行 1 次
this.handleClick = this.handleClick.bind(this) //将原型 this => 实例 this
// 之后在 handClick 方法中,this => 实例 this
}
handle() {
// this => 实例
console.log(this)
}

当然用箭头函数可以完美避免这个问题🤔

setState 的使用 P17

setStatereact实例对象原型链 上的一个方法,可将其内部数据合并到 实例对象state

this.setState({ //setState是一个异步函数,执行 n 次
isHot: "春天来了"
})
console.log(this) // this 为实例对象

类式组件标准写法

//1.创建虚拟DOM
class ClassComponent extends React.Component {
constructor(props) { //构造函数,用于初始化状态,执行 1 次
super(props);
this.state = {
isHot: true,
wind: "习习微风"
};
this.handleClick = this.handleClick.bind(this) //将原型 this => 实例 this
}
handleClick() {
// 方法使用的时机是 原型类实例化之后,所以 this 指向的是实例对象
// 类中的方法开启了局部严格模式 ,this指向的是undefined
this.setState({ //setState是一个异步函数,执行 n 次
isHot: !this.state.isHot,
})
}
render() { //render函数,用于渲染虚拟DOM,执行 n+1 次
return (
<h1 onClick={this.handleClick}>
我是 ClassComponent,今天的天气{this.state.isHot ? "热" : "凉"}
</h1>
);
}
}
//2.渲染虚拟DOM到页面
ReactDOM.render(<ClassComponent />, document.getElementById("test"));

state 的简写方式 P18(类式组件简写方式)

state 写在构造器外边,指向不变

class ClassComponent extends React.Component {
state = { isHot: true }; //初始化状态
handleClick = () => {
this.setState({ isHot: !this.state.isHot });
};
render() {
return (
<h1 onClick={this.handleClick}>
我是 ClassComponent,今天的天气{this.state.isHot ? "热" : "凉"}
</h1>
);
}
}

总结 state P19

理解

state是组件对象最重要的属性, 值是对象(可以包含多个key-value的组合)
组件被称为"状态机", 通过更新组件的state来更新对应的页面显示(重新渲染组件)

  1. 组件中render方法中的this为组件实例对象
  2. 组件自定义的方法中this为undefined,如何解决?
    • 强制绑定this: 通过函数对象的bind()
    • 箭头函数
  3. 状态数据,不能直接修改或更新 setState更新合并

P20~26 React三大属性 props

props 的基本使用 P20

父组件 => 子组件 传递数据的一种方式,跟 vue 的 props 差不多

class ClassComponent extends React.Component {
render() {
const { name, age, sex } = this.props;
return (
<ul>
<li>姓名:{name}</li>
<li>年龄:{age}</li>
<li>性别:{sex}</li>
</ul>
);
}
}
//2.渲染虚拟DOM到页面
ReactDOM.render(
<ClassComponent name="gavin" age="25" sex="男" />,
document.getElementById("test")
);

批量传递 props P21

在标签里传递对象,<ClassComponent {...Obj}/>

原生 js 只能使用展开运算符展开 Arr,不能展开 Obj,此处是 react+babel 的结果

对 props 进行限制 P22

react@16 之前默认生效,之后需要引入单独的包 propTypes

TS 出现之后,这东西的类型限制感觉没什么必要了

Person.propTypes = { //对标签属性进行类型、必要性的限制
name:PropTypes.string.isRequired, //限制name必传,且为字符串
sex:PropTypes.string, //限制sex为字符串
speack:PropTypes.func // 函数
}
Person.defaultProps = { //指定默认标签属性值
sex:'男',//sex默认值为男
age:18
}
const p = {
name: "tom",
age: 18,
speak() {
console.log("我说话了");
},
};
<Person {...p}/> // html

props 的简写方式 P23

react 使用的是不可变数据实现响应式的

类型限制可以写在 类式组件的类 里面

class Person {
static propTypes = { //对标签属性进行类型、必要性的限制
name:PropTypes.string.isRequired, //限制name必传,且为字符串
sex:PropTypes.string, //限制sex为字符串
speack:PropTypes.func // 函数
}
static defaultProps = { //指定默认标签属性值
sex:'男',//sex默认值为男
age:18
}
render(){
return (
// jsx data
)
}
}

类式组件中构造器与 props P24

构造器最大的作用是使用且重构继承父类的构造器方法。

开发时尽量不要写构造器

函数式组件使用 props P25

直接看例子即可

function Person(props) {
const { name, age } = props;
return (
<ul>
<li>{name}</li>
<li>{age}</li>
</ul>
);
}
ReactDOM.render(
<Person name="gavin" age={18} />,
document.getElementById("test")
);

想使用类型限制,直接在函数后添加即可 Person.propTypes={}

总结 props P26

限制 props 类型时注意大小写

P27~31 React三大属性 ref

ref 就是打标识,相当于简化的 ID 属性?用于快速获取标签

字符串形式的 ref P27

String类型的ref 字符串写法可能废弃,存在效率问题,操作DOM?

回调形式的 ref P28

就是将当前节点,以回调的形式挂在 react组件实例对象上。

回调ref中调用次数的问题 P29

每次更新会调用一次回调函数,不会影响功能。

定义class绑定函数可以完美解决这个问题。

开发就写成内联回调,怎么快怎么来

createRef 的使用 P30

react 最推荐的使用方式,书写跟 定义class绑定函数 差不多。

总结 ref P31

尽可能避免 String 类型 Ref 就行了,以下为例子

Ref 例子
export default class RefLearn extends Component {
myRef = React.createRef(); //createRef创建ref空间
handleInput = (c) => {
//类绑定函数
this.input2 = c;
};
handleClick = () => {
console.log("this对象", this)
console.log("String类型ref(尽量避免)----", this.refs.input3.value)
console.log("回调ref----" + this.input1.value);
console.log("类绑定函数----" + this.input2.value);
console.log("createRef----" + this.myRef.current.value);
};
render() {
return (
<div>
<input
placeholder="回调ref写法"
type="text"
ref={(c) => {
this.input1 = c;
console.log("回调ref", c);
}}
/>
<input
placeholder="类绑定函数写法"
type="text"
ref={this.handleInput}
/>
<input placeholder="createRef 写法" type="text" ref={this.myRef} />
<input
placeholder="String类型ref(尽量避免)"
type="text"
ref="input3"
/>
<button onClick={this.handleClick}>点击打印值</button>
</div>
);
}
}

P32 react 中的事件处理

  1. 通过 onXxx 属性 指定事件处理函数(注意大小写)
    • React 使用的是自定义(合成)事件, 而不是使用的原生DOM事件 更好的兼容性
    • React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) 更高效
  2. onXxx 属性 会带过来一个 event.target ,能得到发生事件的DOM元素对象 不要过度使用 ref,追求简洁高效的代码

P33 非受控组件

收集表单数据

现用现取

P34 受控组件

输入框的内容一直管理。相当于 vue 的双向数据绑定

P35 高阶函数-函数柯里化

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

  1. 若A函数,接收的参数是一个函数,那么A就可以称之为高阶函数。
  2. 若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数。

常见的高阶函数有:Promise、setTimeout、arr.map() 等等

函数的柯里化:

通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式。

function sum(a){
return(b)=>{
return (c)=>{
return a+b+c
}
}
}

P36 不用柯里化的写法

传参之前想办法将参数处理一下

<input change={(event)=>{return sum(event.value,b)}}/>

然后参数同时处理 function sum(a,b,c){ return a+b+c }

P37~47 React 生命周期

引出生命周期 ---P37

  1. 组件从创建到死亡它会经历一些特定的阶段。
  2. React组件中包含一系列勾子函数(生命周期回调函数), 会在特定的时刻调用。
  3. 我们在定义组件时,会在特定的生命周期回调函数中,做特定的工作。

旧的生命周期 ---P38~42

旧生命周期图

左边为组件挂载流程,右边为组件更新流程

1. 初始化阶段: 由ReactDOM.render()触发---初次渲染
1. constructor()
2. componentWillMount()
3. render()
- componentDidMount() =====> 常用
一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息
2. 更新阶段: 由组件内部this.setSate()或父组件render触发
1. shouldComponentUpdate()
2. componentWillUpdate()
3. render() =====> 必须使用的一个
4. componentDidUpdate()
3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
- componentWillUnmount() =====> 常用
一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息
  • setState 流程
    shouldComponentUpdate( return true) 控制组件更新
  • forceUpdate 流程
    forceUpdate() 强制更新
  • render 流程

常用重要的钩子

  • componentDidMount() 初始化操作,发送请求,开启定时器等
  • componentWillUnmount() 关闭定时器,取消订阅等
  • render() 渲染更新页面结构

新旧生命周期对比 ---P43

react@17 中,UNSAFE_ 表示有可能废除,在未来版本有可能有 bug,且这三个钩子不常用,被滥用

新生命周期图

多了个 getSnapshotBeforeUpdate

新的生命周期 ---P44~47

1. 初始化阶段: 由ReactDOM.render()触发---初次渲染
1. constructor()
2. getDerivedStateFromProps
3. render()
- componentDidMount() =====> 常用
一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息
2. 更新阶段: 由组件内部this.setSate()或父组件重新render触发
1. getDerivedStateFromProps
2. shouldComponentUpdate()
3. render()
4. getSnapshotBeforeUpdate
5. componentDidUpdate()
3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
- componentWillUnmount() =====> 常用
一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息
  • getDerivedStatetFormProps() 得到 props 的派生属性,state 的值由 props 决定。了解即可,没啥意义
  • getSnapshotBeforeUpdate() 获取组件更新之前的信息,如 滚动位置,这个钩子的作用相当于 vue2 的 beforeUpdated()

P48 DOM 的 diff 算法

P49 初始化 react 脚手架

npm 安装 react 脚手架

现在都流行用 vite 创建 react 项目

P50 脚手架文件介绍 public

P53 样式的模块化

让组件间的样式不冲突,vue 可在 style 标签配置 scope 字段进行限制 <style scope></style>

react 解决组件同名样式冲突的方式

一、可以使用预编译语言 sass\less,样式嵌套来解决

二、利用ES6模块化思路,引入的时候

  • 将组件样式名称修改为 index.module.css
  • 引入的时候 import hello from 'index.module.css'
  • 在使用时,<h3 className={hello.title}></h3>

P54 vscode 中 react 插件的安装

react 插件快速生成结构 rcc rfc

P55 组件化编码流程

  1. 合理拆分组件

  2. 实现静态组件界面效果

  3. 实现动态组件

    • 动态展示初始数据
    • 设计交互

P56~64 TodoList 案例

P75~93 react-router-dom@5

此处是 react-router@5 版本

对路由的理解 ---P75

前端路由原理 ---P76

通过 windown.history 对象 实现路由操作

React 中路由的基本使用 ---P77

  • yarn add react-router-dom@5

  • 明确好界面中的导航区、展示区,之后

    1. 导航区的a标签改为Link标签
      <Link to="/xxxxx">Demo</Link>
    2. 展示区写Route标签进行路径的匹配
      <Route path='/xxxx' component={Demo}/>
    3. <App> 的最外侧包裹了一个<BrowserRouter><HashRouter>

路由组件与一般组件 ---P78

  • 页面上的是一般组件
  • 控制路由匹配显示的是路由组件

路由组件能收到 props.history 对象

路由组件与一般组件的对比

  • 写法不同:
    一般组件:<Demo/>
    路由组件:<Route path="/demo" component={Demo}/>
  • 存放位置不同:
    一般组件:components
    路由组件:pages
  • 接收到的 props 不同:
    一般组件:写组件标签时传递了什么,就能收到什么
    路由组件:接收到 history对象的三个固定的属性

history 对象信息

history:
go: ƒ go(n)
goBack: ƒ goBack()
goForward: ƒ goForward()
push: ƒ push(path, state)
replace: ƒ replace(path, state)
location:
pathname: "/about"
search: "" // search 传数据
state: undefined // state 传数据
match:
params: {} // params 传数据
path: "/about"
url: "/about"

实现点击追加高亮效果,点击追加 active属性样式

对原生路由组件进行封装,props 数据传过去就行了

组件标签体的内容,如何传给组件?

  • vue2 有插槽
  • 组件 this.props.children 可以收集到 父组件在自定义组件中写的内容

Switch 的使用 ---P81

增加匹配效率,点到即止不再匹配

通常情况下,path 和 component 是一一对应的关系,Switch 可以提高路由匹配效率(单一匹配)。

解决样式丢失问题 ---P82

二级路径时,刷新请求资源会改变,导致页面使用资源失效

如果请求资源不存在,返回 index.html

  • 相对路径删掉
    public/index.html 中 引入样式时不写 ./写 / (常用)
  • 引用,三方css用绝对路径
    public/index.html 中 引入样式时不写 ./写 %PUBLIC_URL% (常用)
  • HashRouter 解决

路由的严格匹配与模糊匹配 ---P83

包管理器尽量使用 yarn,不要混用,混用可能出现 bug

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

Redirect的使用 ---P84

上来选中 About 组件

一般写在所有路由注册的最下方,当所有路由都无法匹配时,跳转到 Redirect 指定的路由

具体编码:

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

嵌套路由(多级路由) ---P85

注册子路由时要写上父路由的 path 值

<Route path="/home/news" component={News}/>

路由的匹配是按照注册路由的顺序进行的

向路由组件传递参数 ---P86~89

params参数

  • 路由链接(携带参数):
    <Link to='/demo/test/tom/18'>详情</Link>
  • 注册路由(声明接收):
    <Route path="/demo/test/:name/:age" component={Test}/>
  • 接收参数:this.props.match.params

search参数

  • 路由链接(携带参数):
    <Link to='/demo/test?name=tom&age=18'>详情</Link>
  • 注册路由(无需声明,正常注册即可):
    <Route path="/demo/test" component={Test}/>
  • 接收参数:this.props.location.search

备注:获取到的search是urlencoded编码字符串,需要借助querystring解析

state参数

  • 路由链接(携带参数):
    <Link to={{pathname:'/demo/test',state:{name:'tom',age:18}}}>详情</Link>
  • 注册路由(无需声明,正常注册即可):
    <Route path="/demo/test" component={Test}/>
  • 接收参数:this.props.location.state

备注:刷新也可以保留住参数

push 与 replace ---P90

在跳转标签上添加 <Link replace={true}/> 实现覆盖式跳转

编程式路由导航 ---P91

借助 this.props.history 对象上的API对操作路由 跳转、前进、后退

- this.props.history.push()
- this.props.history.replace()
- this.props.history.goBack()
- this.props.history.goForward()
- this.props.history.go()

withRouter 的使用 ---P92

让一般组件可以路由跳转
因为只有路由组件有 History 对象

import { withRouter } from 'react-router-dom';
export default withRouter(Header)

BrowserRouter与HashRouter的区别 ---P93

底层原理不一样:

  • BrowserRouter使用的是H5的history API,不兼容IE9及以下版本。
  • HashRouter使用的是URL的哈希值。

path表现形式不一样

  • BrowserRouter的路径中没有#,
    例如:localhost:3000/demo/test
  • HashRouter的路径包含#,
    例如:localhost:3000/#/demo/test

刷新后对路由state参数的影响

  • BrowserRouter 没有任何影响,因为state保存在history对象中。
  • HashRouter 刷新后会导致路由state参数的丢失!!!

备注:HashRouter可以用于解决一些路径错误相关的问题。

react-redux P97~114

redux 简介 ---P97

redux 工作流程 ---P98

求和案例 ---P99-100

redux 理解 ---103

P116~126 React 新特性(Hooks 方法式组件写法)

setSate ---P116

  1. 对象式 setState setState(stateChangeObj, [callback] )
  2. 函数式 setState ,能看到 stateprops
this.setState((state,props)=>{
return 对象
}, [callback])
  • setState() 是同步行为, 引起的动作更新页面是异步行为,方法回调callback在最异步行为完成后运行
  • 对象式写法是函数式写法的语法糖

lazyLoad ---P117

组件实现路由懒加载

Suspense

  1. 先引入 import {lazy,Susoense} from 'react'
  2. 对要懒加载的组件进行定义 const Home = lazy(()=> import('./Home'))
  3. 在 jsx 中使用组件
<Suspense fallback={ <h3>加载中</h3>} >
<Route path='/home' component={Home} />
</Suspense>

setHooks ---P118~120

react@16.8 之后
在函数式组件中使用 react 特性

函数式组件求和案例

Demo(){}
相当于render

React Hook/Hooks是什么?

(1). Hook是React 16.8.0版本增加的新特性/新语法
(2). 可以让你在函数组件中使用 state 以及其他的 React 特性

三个常用的Hook

  1. State Hook: useState()
    使用三大属性 State
  2. Effect Hook: useEffect()
    使用 React 生命周期
  3. Ref Hook: useRef()
    使用三大属性 Ref

State Hook

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

useState() 说明:

  • 参数: 第一次初始化指定的值在内部作缓存
  • 返回值: 包含2个元素的数组, 第1个为内部当前状态值, 第2个为更新状态值的函数

setXxx() 2种写法:

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

代码示例

import { useState } from "react"; //引入
export default function MyState() {
const [count, setCount] = useState(1);; // 使用
function handle() {
setCount((count) => count + 1);
}
}

Effect Hook

Effect Hook 可以让你在函数组件中执行副作用操作
(用于 模拟类组件中的生命周期钩子)

React中的副作用操作:

  • 发ajax请求数据获取
  • 设置订阅 / 启动定时器
  • 手动更改真实DOM

代码示例

import { useState, useEffect } from "react";
export default function MyLife() {
const [now, setNow] = useState(0);
useEffect(() => { //使用副作用
// 在此可以执行任何带副作用操作
//componentDidMount(),componentDidUpdate()
let Timer = setInterval(() => {
setNow((now) => now + 1);
}, 1000);
return () => {
// 在组件卸载前执行一些收尾工作 componentWillUnmount
clearInterval(Timer);
};
}, [0]); // 如果指定的是[], 回调函数只会在第一次render()后执行
}

可以把 useEffect Hook 看做如下三个函数的组合

  • componentDidMount()
  • componentDidUpdate()
  • componentWillUnmount()

Ref Hook

在函数组件中存储/查找组件内的标签或任意其它数据
作用: 保存标签对象,功能与 React.createRef() 一样

代码示例

import { useRef } from "react";
export default function MyRefs() {
const inputVal = useRef();
return (
<input type="text" ref={inputVal} />
)
}

Fragment ---P121

Fragment 碎片,不用写根标签了 跟 vue3 一样,少了一层

代码示例

<Fragment><Fragment>
<></>

Context ---P122 🤔🤔🤔

Context 上下文
用于【祖组件】===>【后代组件】间通信

使用

1) 创建Context容器对象:
const XxxContext = React.createContext()
2) 渲染子组时,外面包裹xxxContext.Provider, 通过value属性给后代组件传递数据:
<xxxContext.Provider value={数据}>
子组件
</xxxContext.Provider>
3) 后代组件读取数据:
//第一种方式:仅适用于类组件
static contextType = xxxContext // 声明接收context
this.context // 读取context中的value数据
//第二种方式: 函数组件与类组件都可以
<xxxContext.Consumer>
{
value => ( // value就是context中的value数据
要显示的内容
)
}
</xxxContext.Consumer>

注意:在应用开发中一般不用context, 一般都它的封装react插件

PureComponent ---P123

Component的问题

只要执行setState(),即使不改变状态数据, 组件也会重新render(),也会自动重新render子组件,效率低

效率高的做法: 只有当组件的state或props数据发生改变时才重新render()

原因: Component 中的 shouldComponentUpdate() 总是返回true

解决

  • > 一、 重写shouldComponentUpdate()方法
    比较新旧state或props数据, 如果有变化才返回true, 如果没有返回false
  • >二、 使用PureComponent
    PureComponent重写了shouldComponentUpdate(), 只有state或props数据有变化才返回true
    注意:
    • 只是进行state和props数据的浅比较, 如果只是数据对象内部数据变了, 返回false
    • 不要直接修改state数据, 而是要产生新数据
  • 项目中一般使用 PureComponent 来优化
  • react 实现响应式使用的是不可变数据,所以修改数据要覆盖原数据

renderProps ---P124

react 实现插槽功能

  • Vue中:
    使用slot技术, 也就是通过组件标签体传入结构 <AA><BB/></AA>
  • React中:
    • 使用children props: 通过组件标签体传入结构,存在问题
    • 使用render props: 通过组件标签属性传入结构, 一般用render函数属性

children props

<A>
<B>xxxx</B>
</A>
{this.props.children}

问题: 如果B组件需要A组件内的数据, 做不到

render props

<A render={(data) => {<C data={data}></C>} }></A>

A组件: {this.props.render(内部state数据)}
C组件: 读取A组件传入的数据显示 {this.props.data}

ErrorBoundary ---P125

防止容易发生的错误导致页面出不来

理解 错误边界:用来捕获后代组件错误,渲染出备用页面

特点

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

使用方式

getDerivedStateFromError 配合 componentDidCatch

// 生命周期函数,一旦后台组件报错,就会触发
static getDerivedStateFromError(error) {
console.log(error);
// 在render之前触发
// 返回新的state
return {
hasError: true,
};
}
componentDidCatch(error, info) {
// 统计页面的错误。发送请求发送到后台去
console.log(error, info);
}

React 组件间通信方式总结 ---P126

  • 父子组件
  • 兄弟组件
  • 跨级组件
  1. props,传函数方法
  2. ref
  3. pubsub
  4. redux
  5. context,封装插件

P127 React Router @6

vue router5 是为了新的组合式api 服务的
react router 6 是为了函数式组件服务的

路由表的使用 ---P131

"react-router-dom": "^6.10.0"

创建 routes 文件,写好配置

使用 useRoutes 钩子

多级路由 ---P132

import { Outlet } from "react-router-dom";
export default function index() {
return (<Outlet/>)
}

路由的 params 参数 ---P133

作用:回当前匹配路由的params参数,类似于5.x中的match.params

示例代码

路由表 path 写参数进行占位,父组件写路由跳转时传参数, 子组件收值

// 路由表占坑
path: "rparams/:id/:age/:sex",
// 路由组件收参
import { useParams,useMatch } from 'react-router-dom';
function ProfilePage() {
let { name } = useParams(); // 获取URL中携带过来的params参数
}
// 父组件传参
<NavLink to={`rparams/${name}/${age}/${sex}`} >

路由的 search 参数 ---P134

不用路由表占位,直接在父组件里定义好即可,然后子组件接收

// 父组件传参
<NavLink to={`rparams?name=${name}&age=${age}&sex=${sex}`}>A</NavLink>
// 子组件收参
import { useSearchParams,useLocation } from "react-router-dom";
export default function MyParams() {
//setSearch 更新地址栏收到的参数
const [search, setSearch] = useSearchParams();
const name = search.get("name");
const age = search.get("age");
const sex = search.get("sex");
}

路由的 State 参数 ---P135

利用 useLocation 对象,最好用的方法

// 父组件传值
<NavLink to="rparams" state={{ name, age, sex }}>Params</NavLink>
// 子组件接收
import { useLocation } from "react-router-dom";
export default function MyParams() {
const { state: { name, age, sex }, } = useLocation();
}

编程式路由导航 ---P136

import { useNavigate } from "react-router-dom";
export default function index() {
const navigate = useNavigate();// 仅支持 携带State 参数
handleRoute = (e) =>{
navigate('route', {
replace: false,
state: {
id: 1,
title: "One",
content: "isContent",
},
});
}
}

useRouterContext ---P137

USE: 用来判断组件是否被路由管理

​作用:如果组件在 的上下文中呈现,则 useInRouterContext 钩子返回 true,否则返回 false。

useNavigationType

USE: 获取用户是如何来到当前页面的

作用:返回当前的导航类型。

返回值:POP、PUSH、REPLACE。

备注:POP 是指在浏览器中直接打开了这个路由组件(刷新页面来的)。

useOutlet()

作用:用来呈现当前组件中渲染的嵌套路由。

示例代码:

const result = useOutlet()
console.log(result)
  • 如果嵌套路由没有挂载,则result为null
  • 如果嵌套路由已经挂载,则展示嵌套的路由对象

useResolvedPath()

给定一个 URL值,解析其中的:path、search、hash值。

总结 P141

react-router@6 的设计目的,是为了函数式组件服务的,所以 5 的东西,6都写成了可以按需引入的形式,提高了开发人员的效率

本文作者:悠悠江水

本文链接:https://www.cnblogs.com/isgavin/p/17274566.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   悠悠江水  阅读(433)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起