type
status
date
slug
summary
tags
category
icon
password
React简单介绍
- ‣
- React是由Meta公司研发,是一个用于构建Web和原生交互界面的库。
- 优势
- 组件化的开发方式:提高开发效率和组件复用性
- 不错的性能
- 采用虚拟dom,最大限度的减少与DOM的交互,速度快。
- 丰富的生态
- 跨平台支持
- React Native和Expo可使用React构建Andrio、iOS等应用程序
相较于传统基于DOM开发的优势
相较于其它前端框架的优势
使用JSX,代码的可读性很好。
可以与已有的库或者框架很好的配合。
使用React构建组件使代码更加容易得到复用,可以很好的应用在大型项目的开发过程中。
单向数据流减少了重复的代码,轻松实现数据绑定。
搭建开发环境
- create-react-app是一个快速创建React开发环境的工具,底层由Webpack构件,封装了配置细节,开箱即用.
- npx :Node.js工具命令,查找并执行后续的包命令。
- create-react-app :核心包(固定写法),用于创建React项目。
- react-basic :React项目的名称(可以自定义)
- 更多的创建方式‣
- cd +文件名,切换到文件夹下,使用 npm start 启动项目
- 项目文件
- package.json 项目的依赖配置管理文件(有点类似于 Maven 中 pom)
- ‣
- ‣
- src(核心源码)
src
(source 的缩写)目录是存放项目源代码的地方。在 React 项目中,这是开发的核心区域。在这里,你会编写 React 组件、样式文件、业务逻辑等代码。- index.js
- 项目的入口
- App.js
- 项目的根组件
- node_modules
- node_modules目录是npm(Node Package Manager)在安装项目依赖时创建的目录。它用于存放项目所依赖的所有外部包(包括 JavaScript 库、工具等)。这些依赖包是根据package.json文件中的dependencies和devDependencies配置来安装的。 依赖包存储:当你在项目中运行npm install命令时,npm会从远程的npm仓库(如https://www.npmjs.com/)下载所需的依赖包,并将它们解压和安装到node_modules目录中。例如,如果你在package.json中有react作为依赖,npm会将react包及其相关的文件安装到node_modules/react目录下。这个目录下包含了react库的 JavaScript 代码、类型定义文件(如果有)等内容,这些代码可以被项目中的其他代码引用。 依赖树结构:node_modules目录具有复杂的依赖树结构。一个依赖包可能还依赖于其他的包,npm会自动解析这些依赖关系并安装所有必需的包。例如,react-dom依赖于react,当你安装react-dom时,npm会确保react也被正确安装,并且react-dom可以访问到react的代码。这种依赖树可能会很深,包含许多不同的包及其版本。不过,现代的构建工具和npm机制会尽量优化这个结构,以减少冗余和提高性能。
- public
public
目录主要用于存放项目的静态资源,这些资源在构建和部署后会原封不动地提供给用户访问。它是项目的公共资源目录,包括 HTML(index.html等) 文件、图标文件、字体文件等。
JSX基本语法
什么是jsx
JSX是JavaScript和XML(HTML)的缩写,表示在JS代码中编写HTML模板结构,它是React中编写UI模板的方式。
优势:
1、HTML的声明式模板写法
2、JS的可编程能力
jsx的本质
JSX并不是标准的JS语法,它是JS的语法扩展,浏览器本身不能识别,需要通过解析工具做解析之后才能在浏览器中运行。
jsx基础-高频场景
JSX中使用JS表达式
在JSX中可以通过 大括号语法{} 识别JavaScript中的表达式,比如常见的变量、函数调用、方法调用等等。
- 使用引号传递字符串
- 使用JavaScript变量
- 函数调用和方法调用
- 使用JavaScript对象
案例
上面创建js对象是通过字面量的方式创建的对象
对象字面量创建对象
优点:
- 简单直观:
- 对象字面量是创建简单对象的最直接方式。例如,
let person = { name: 'John', age: 30 };
,代码非常简洁明了,很容易理解。可以直接看到对象的属性和对应的值,对于快速创建包含少量属性的对象非常方便。
- 易于阅读和维护:
- 因为其语法简单,所以对于其他开发人员来说,阅读和理解代码的成本较低。不需要了解复杂的构造函数或类的概念,就能够明白对象的结构和内容。
- 灵活性高:
- 可以在创建对象时直接初始化各种数据类型的属性,包括基本数据类型(如数字、字符串)和复杂数据类型(如数组、其他对象)。例如,
let car = { brand: 'Toyota', colors: ['red', 'blue'], features: { hasAirbag: true, hasSunroof: false } };
。
缺点:
- 不适合创建大量相似对象:
- 如果需要创建许多具有相同属性结构的对象(例如,创建多个表示学生的对象,都有姓名、学号、成绩等属性),使用对象字面量会导致代码重复。每创建一个新对象都需要重新编写属性名,这会使代码变得冗长且难以维护。
- 缺乏复用性和封装性:
- 没有办法像构造函数或类那样,在对象之间共享方法或者进行属性的封装。例如,不能方便地为所有通过对象字面量创建的对象添加一个通用的方法来计算属性之间的关系(如计算个人所得税的方法)。
在react的{}中可以用上述的表达式,但是返回的数据或者展示的数据必须是有效的react元素
在 React 中,有效的 React 元素是可以被渲染为 UI 的对象。这些元素可以是基本的 HTML 标签、React 组件或者它们的组合。有效的 React 元素的特点包括:
- 基本 HTML 元素:比如
<div>
,<span>
,<h1>
等。这些元素会被渲染为相应的 HTML 元素。
- React 组件:自定义的 React 组件也可以作为有效的元素。它们可以是类组件或函数组件。
- 组合元素:可以将多个元素组合在一起,形成一个树状结构。
- Fragment:React 提供的
Fragment
组件可以让你返回多个子元素,而不需要额外的 DOM 节点。
- 字符串和数字:可以直接渲染字符串和数字,这会作为文本节点渲染。
- 数组:使用
map
等方法生成的包含有效 React 元素的数组。例如:
JSX中实现列表渲染
语法: 在JSX中可以使用原生JS中的map方法遍历渲染列表。
案例:
注意:
在React中,渲染list的时候,重复的元素身上需要绑定一个独一无二的key值。(上面的key = item.id) key的作用:react框架内部使用,提升更新性能.
关于map方法
- map是 JavaScript 数组的一个高阶函数。它的主要作用是对数组中的每个元素进行操作,并返回一个新的数组,新数组中的元素是原数组元素经过操作后的结果. 例如,给定一个数组numbers = [1, 2, 3],如果想得到每个元素的平方组成的新数组,可以使用map函数:let squaredNumbers = numbers.map((number) => number * number);,这样squaredNumbers就会是[1, 4, 9]。
‣
JSX中实现条件渲染
语法:在React中,可以通过逻辑与运算符&&、三元表达式(?:)实现基础的条件渲染。
案例一
逻辑与运算符的其他运算规则是什么?
1. 假值判断规则
- 逻辑与运算符(
&&
)首先会检查左边操作数的值。如果左边操作数是假值(在 JavaScript 中,假值包括false
、0
、null
、undefined
、""
(空字符串)、NaN
),那么整个逻辑与表达式的值就为左边的假值,并且右边操作数不会被计算。
- 例如:
let result1 = false && console.log("This won't be executed");
,在这里,因为左边是false
,所以整个表达式的值为false
,并且console.log
语句不会被执行。let result2 = 0 && "Some value";
,由于0
在 JavaScript 中被视为假值,所以result2
的值为0
,而"Some value"
不会被考虑。
2. 真值判断规则(除了上述提到的假值,其他值都被视为真值)
- 如果左边操作数是真值(如非零数字、非空字符串、对象、数组等),那么整个逻辑与表达式的值由右边操作数决定。此时右边操作数会被计算。
- 例如:
let result3 = 5 && "Another value";
,因为5
是真值,所以会计算右边的操作数,result3
的值为"Another value"
。let result4 = [1, 2, 3] && "Array is truthy";
,数组[1, 2, 3]
是真值,所以result4
的值为"Array is truthy"
。
- 短路特性
- 逻辑与运算符具有短路特性。这意味着当左边操作数为假值时,右边操作数不会被执行或计算。这在一些情况下非常有用,比如在条件判断中避免不必要的计算.
- 例如,在一个函数中,如果要检查某个条件是否满足才执行另一个可能耗时的操作:
- 当
condition
为false
时,timeConsumingFunction
不会被调用,从而节省了计算资源。
关于逻辑与或
let result = true && "This will be executed";
在这段代码中,
result
的值为 "This will be executed"
。原因如下:
当使用逻辑与运算符
&&
时,如果左侧操作数为真值,那么整个表达式的值为右侧操作数的值。在let result = true && "This will be executed";
中,左侧操作数true
是真值,所以继续计算右侧操作数,右侧操作数是字符串"This will be executed"
,因此整个表达式的值为这个字符串,所以result
被赋值为"This will be executed"
。案例二
需求:列表中需要根据文章状态适配三种情况,单图,三图和无图三种模式。
解决方案:自定义函数 + if判定语句
事件绑定
基本事件绑定
在 JSX 中,可以通过将事件处理函数作为属性传递给元素来绑定事件。通常,事件的命名方式是小写字母
on
开头,后面跟随事件名,使用驼峰命名法。语法:on + 事件名称 = { 事件处理程序 } ,整体上遵循驼峰命名法。
注意事项
- 事件处理函数:事件处理函数应该是一个函数引用,而不是函数调用。即不要加括号:
- 传递参数:如果需要在事件处理函数中传递参数,可以使用箭头函数或
bind
方法: - 箭头函数更简洁,适合小规模应用或不需要频繁重新渲染的场景
- 箭头函数写在JSX中会在每次渲染时创建一个新函数,可能会有轻微的性能影响,通常只有在有大量列表或高频率更新时才需特别关注。
- 箭头函数(Arrow Functions)是ES6引入的一种更简洁的函数写法,主要用于简化代码,并且有一些独特的特性,比如不绑定自己的
this
值 - 基本语法
- 如果只有一个参数,可以省略圆括号
()
: - 如果没有参数,需要加上空的
()
: - 表达式(Expression):
- 表达式是任何能产生一个值的代码片段。
- 表达式可以出现在需要值的地方,例如赋值、条件判断等。
- 语句(Statement):
- 语句是执行某个操作的代码块,通常用于控制程序的流程。
- 语句不直接产生值,而是完成某个动作或指令。
- 表达式的特性:
- 产生值:表达式总是返回一个值,可以是数字、字符串、布尔值、对象等。
- 可嵌套:表达式可以嵌套在其他表达式中,形成更复杂的计算。
- 可用在赋值中:表达式的结果可以直接赋值给变量。
- 语句的特性:
- 执行操作:语句通常执行某种操作,而不是返回一个值。
- 控制结构:语句可以用来控制程序的执行流程,例如条件语句、循环语句等。
- 不产生值:虽然语句可能包含表达式,但语句本身并不返回任何有意义的值。
- 基本数学表达式:
- 函数调用:
- 条件表达式(三元运算符):
- 变量声明语句:
- 条件语句:
- 循环语句:
- 表达式可以出现在任何需要值的地方:
- 例如,作为函数参数、赋值、条件判断等。
- 语句用于控制程序流:
- 例如,定义逻辑条件、循环迭代、处理错误等。
- 函数调用作为表达式
- 函数调用作为语句
- 作为表达式:当函数调用用于计算一个值,或作为其他表达式的一部分时,它是表达式。例如:
const result = square(5);
- 作为语句:当函数调用仅用于执行某些操作,而不关心返回值时,它是语句。例如:
greet();
- 表达式总是返回一个值,可以在任何需要值的地方使用。
- 语句执行某种操作,不直接返回值,主要用于控制程序的流程。
- 回调函数:如事件监听、数组操作、Promise 等异步操作。
- 保持
this
绑定:在需要使用外层this
的情况下,可以用箭头函数避免手动绑定。 - 简化短小函数:箭头函数语法简洁,适合定义简单逻辑的函数。
this.handleClick.bind(this, param1, param2)
使用bind
方法创建了一个新的函数,这个函数的第一个参数this
是组件实例的上下文,后面的参数param1
和param2
是需要传递的自定义参数。- 这样当
onClick
触发时,绑定的函数会自动传递这些参数给handleClick
。 bind
会立即返回一个绑定了this
和参数的新函数,所以每次渲染时,这个绑定动作都会重新进行,可能会影响性能。为了优化性能,你可以在构造函数中绑定事件,避免每次渲染重复绑定。- 复杂场景:在构造函数中预先使用
bind
更有效率,适合需要高性能的场景
关于箭头函数
箭头函数的基本用法
箭头函数的基本语法形式如下:
当函数体只有一行返回值时,可以进一步简化为:
[补充]表达式和语句的区别(更深的情况需要了解编译原理了)
1. 定义
2. 特性
3. 示例
表达式示例
在以上例子中,所有的代码片段都是表达式,因为它们都计算并返回了一个值。
语句示例
在这些例子中,所有的代码片段都是语句,因为它们执行某个操作,而不是返回值。
4. 如何在 JavaScript 中使用
函数调用在 JavaScript 中可以既作为表达式,也可以作为语句,这取决于如何使用它。让我们更详细地探讨这个概念。
当函数被调用并用于计算一个值或作为其他表达式的一部分时,它被视为表达式。此时,函数的返回值是一个有意义的结果,可以用于赋值或其他计算中。
示例:
在这里,
square(5)
被用作一个表达式,其返回值 25
被赋值给变量 result
。当函数被调用以执行某些操作(例如打印输出、修改状态等),而不关注其返回值时,它被视为语句。在这种情况下,函数的调用只是为了执行该操作。
示例:
在这里,
greet()
被用作语句,它执行 console.log("Hello!")
,但我们并不关心 greet()
的返回值(它是 undefined
)。3. 结合使用
在复杂的代码中,函数调用可以同时作为表达式和语句,取决于上下文。例如,可以在
if
语句中调用一个函数,如果函数返回布尔值,则可以用作条件判断:示例:
在这个例子中,
isEven(4)
是一个表达式,它的返回值 true
被用作 if
语句的条件。4. 总结
5. 总结
理解这两者之间的区别有助于你在编写和阅读代码时,更清楚地知道代码的意图和执行方式。
特性和注意事项
1.
this
的绑定箭头函数和普通函数最大的不同在于:箭头函数不会创建自己的
this
,它会继承外层作用域中的 this
。这个特性非常有用,可以避免在回调函数中手动绑定 this
。示例:
上面的例子中,
this
不会指向 Counter
类的实例。如果用箭头函数来定义 setTimeout
的回调,this
会自动指向外层的实例:2. 简化回调函数
箭头函数通常用于回调函数中,使代码更加简洁。常见场景包括数组的
map
、filter
和 reduce
等方法:3. 不绑定
arguments
对象箭头函数没有
arguments
对象。如果需要访问参数,可以用剩余参数(rest parameter)来代替:4. 不支持
new
关键字箭头函数不能用作构造函数,不能与
new
关键字一起使用,否则会报错。因为箭头函数没有自己的 this
,所以无法作为构造函数实例化对象。5. 不支持 prototype
属性
由于箭头函数没有
prototype
属性,不能作为构造函数来生成原型对象。一般来说,如果函数需要构造一个对象或提供原型方法,不适合用箭头函数。适用场景
更多了解
‣
关于bind
bind
方法可以预先绑定参数,从而在事件触发时传递给事件处理函数:解释:
注意:
- 事件对象:React 会自动将事件对象传递给事件处理函数,允许你访问事件的属性:
- 事件对象+参数都要:在事件绑定的位置传递事件实参e和自定义参数,clickHandler中声明形参,注意顺序对应.
- 合成事件:React 使用合成事件(Synthetic Events),这是对浏览器原生事件的封装,提供了跨浏览器的一致性。
- 清理事件:在类组件中,如果在
componentDidMount
或其他生命周期方法中添加事件监听器,请确保在componentWillUnmount
中清理它们。
React中的组件
- 一个组件就是用户界面的一部分,它可以有自己的逻辑和外观,组件之间可以互相嵌套,也可以复用多次。组件化开发可以让开发者像搭积木一样构建一个完整的庞大的应用。在 React 中,组件是构建用户界面的基本单元。每个组件可以看作是一个独立的、可复用的 UI 模块,负责呈现特定部分的内容,并管理相关的逻辑和数据。React 应用通常由多个组件组成,通过组件的组合来构建完整的用户界面。
- 在React中,一个组件就是首字母大写的函数,内部存放了组件的逻辑和视图UI,渲染组件只需要把组件当成标签书写即可。
- 组件的核心概念
- 组件是独立的:每个组件负责展示和管理自己的那部分 UI,处理自身的数据和交互逻辑,不依赖于其他组件。
- 组件是可复用的:组件设计为可复用的代码块。通过重用相同的组件,你可以避免重复代码,实现统一的设计样式和行为。
- 组件是声明式的:React 组件使用一种声明式语法,即你描述“UI 应该是什么样的”,而不是逐步指示如何构建 UI。React 会根据状态和属性的变化来自动更新 UI。
- 组件的类型
- 函数组件(Function Component)
- 函数组件是使用 JavaScript 函数定义的组件。
- 它们没有
this
,而是直接通过参数props
接收外部数据。 - 通过 React Hooks,可以让函数组件处理状态和副作用。
- 类组件是使用 ES6 类语法定义的组件,继承自
React.Component
。 - 它们可以访问生命周期方法(如
componentDidMount
、componentDidUpdate
)。 - 类组件通过
this.props
访问属性,通过this.state
管理内部状态。
React 中的组件主要分为两种类型:
b. 类组件(Class Component)
useState
聊一下React hooks
React 的核心是组件。v16.8 版本之前,组件的标准写法是类(class).基于类的写法,一个组件类仅仅是一个按钮,它的代码已经很"重"了。真实的 React App 由多个类按照层级,一层层构成,复杂度成倍增长。再加入 Redux,就变得更复杂。
React 团队希望,组件不要变成复杂的容器,最好只是数据流的管道。开发者根据需要,组合管道即可。 组件的最佳写法应该是函数,而不是类。但是,这种写法有重大限制,必须是纯函数,不能包含状态,也不支持生命周期方法,因此无法取代类。React Hooks 的设计目的,就是加强版函数组件,完全不使用"类",就能写出一个全功能的组件。
Hook 这个单词的意思是"钩子"。React Hooks 的意思是,组件尽量写成纯函数,如果需要外部功能和副作用,就用钩子把外部代码"钩"进来。 React Hooks 就是那些钩子。
你需要什么功能,就使用什么钩子。React 默认提供了一些常用钩子,你也可以封装自己的钩子。所有的钩子都是为函数引入外部功能,所以 React 约定,钩子一律使用
use
前缀命名,便于识别。你要使用 xxx 功能,钩子就命名为 usexxx。React 默认提供的四个最常用的钩子。
useState() useContext() useReducer() useEffect()
React的Hook函数可以看成是一组工具,它们帮助我们在函数组件里实现以前只能在类组件中使用的一些功能,比如管理状态和生命周期。
Hook的几个小特点:
- 只能在函数组件或自定义Hook里调用,不能在普通函数中使用。
- 不能在条件语句或循环中调用,必须在组件的最顶层调用,保证每次渲染时Hook的顺序一致。
React Hook的设计初衷就是为了让函数组件也能拥有“类组件”的功能,同时让代码更简洁和灵活,在没有React Hook之前,类组件虽然能实现功能,但代码会相对复杂。我们用React Hook的原因就是让代码更简洁、逻辑更清晰。
不用Hook可以实现什么?
在类组件中,不用Hook确实也可以实现组件状态管理和生命周期控制,但要写很多额外代码,比如构造函数、
this
关键字绑定、生命周期方法(如componentDidMount
、componentDidUpdate
等)。这使得代码看起来复杂,而且容易出错。有了Hook,组件代码更简洁
- Hook的函数式写法更简单:相比类组件,函数组件没有
this
、没有复杂的生命周期函数,代码量更少。
- 逻辑更清晰:Hook让我们可以更灵活地组织逻辑,把相关的状态和效果放在一起,代码更易读,维护起来也更方便。
类组件和函数组件+Hook实现同样功能时的代码对比
这个计数器会显示一个数字,每点击一次按钮数字就会加1。我们用类组件和函数组件+Hook分别实现这个功能。
1. 用类组件实现计数器
解释:
- 这里需要定义一个构造函数(
constructor
),来初始化状态count
。
- 用
this.setState
来更新状态,这会导致组件重新渲染。
handleClick
是一个事件处理函数,用于点击按钮时更新count
。
this
关键字的使用在类组件中很常见,容易出错,尤其是对初学者。
2. 用函数组件+Hook实现计数器
解释:
- 用
useState(0)
来初始化状态,这里count
是状态值,setCount
是更新状态的函数。
- 点击按钮时直接调用
setCount(count + 1)
更新状态,无需this
关键字。
- 没有构造函数,逻辑直接写在组件中,代码更加简洁明了。
总结对比
类组件实现 | 函数组件 + Hook 实现 |
需要构造函数初始化状态 | 不需要构造函数 |
用 this.state 和this.setState 管理状态 | 用 useState 直接管理状态 |
需要用 this 关键字调用方法 | 无需 this 关键字,函数式写法更简单 |
代码相对繁琐,逻辑分散 | 代码简洁,逻辑集中 |
如果函数组件不用Hook的话,就无法实现状态管理和副作用处理,因此也就无法实现动态更新的效果.
如果我们用不用Hook的函数组件来实现计数器,它将无法正常工作。来看个示例:
效果:
- 每次点击按钮,
count
变量的值确实在handleClick
里增加,但组件不会重新渲染,因此页面上看到的count
值始终是0,完全没法实现动态更新。
- 函数组件没有状态机制,无法“记住”
count
的值,点击按钮后,UI不会重新渲染
在类组件中继承
Component
后,我们确实可以实现很多像render
这样的特定方法。这些方法被称为生命周期方法和必需方法,render
就是其中一个关键的必需方法,render
方法的作用在类组件中,
render
是必须要实现的方法。它的作用是返回组件的UI结构,也就是告诉React这个组件要如何展示在页面上,每当组件的state
或props
变化时,render
方法会被重新调用,更新界面。useState是一个React Hook(函数),它允许我们向组件添加一个状态变量,从而控制影响组件的渲染结果。
本质:和普通变量不同的是,状态变量一旦发生变化组件的视图UI也会跟着变化(数据驱动视图)。
1、useState是一个函数,返回值是一个数组。
2、数组中的第一个参数是状态变量,第二个参数是set函数用来修改状态的变量。
3、useState的参数将作为count的初始值 。
案例:点击按钮,数值增加。
修改状态的规则
- 状态不可变:在React中,状态被认为是只读的,应该始终替换它而不是修改它,直接修改状态.react期望我们使用不可变数据。不可变数据的概念是每次修改状态时,创建一个新对象,而不是直接修改原有对象,这样React能清晰地检测到变化
- 修改对象状态:对于对象类型的状态变量,应该始终传给set方法一个全新的对象来进行修改。直接修改原对象,不引发视图变化。
- 函数参数:可以直接解构函数的参数。
- 交换变量:可以用解构轻松交换两个变量的值。
...person
:使用解构语法,将person
对象的所有属性复制到updatedPerson
中。name: "Bob"
:覆盖person
中的name
字段,使updatedPerson
中的name
变为"Bob"
。- 这种方式确保原始对象
person
不会被修改,而是生成了一个新对象updatedPerson
。 - 对象解构更新:使用
...
解构原对象,再指定需要更新的字段即可。 - 数组解构更新:可以使用解构来构建新数组,也可以结合
map
、filter
等数组方法实现更复杂的更新逻辑。
这里先用展开运算符做个拷贝,然后再用后面重复属性进行替换即可.
解构
构赋值是ES6引入的一种便捷语法,可以从对象或数组中快速提取数据并赋值给变量,这种方法可以让代码更简洁明了.
解构赋值的基本用法
1. 对象解构赋值
对象解构赋值可以从对象中提取指定的字段,然后赋值给新的变量。
基本例子
在这里,
{ name, age }
会从person
对象中提取出name
和age
字段,并创建同名变量。给变量重命名
如果想提取字段并给它起个新名字,可以这样写:
默认值
当我们提取的属性不存在时,可以给变量设置一个默认值:
2. 数组解构赋值
数组解构赋值允许我们按照顺序提取数组中的元素,并赋值给变量。
基本例子
数组解构赋值是基于位置的,所以我们可以很方便地提取特定位置的元素。
跳过元素
如果不需要数组中的某个值,可以跳过它:
使用默认值
和对象解构一样,数组解构也支持默认值:
3. 嵌套解构
对象和数组可以嵌套解构,这在处理复杂结构的数据时特别有用。
对象中的嵌套解构
数组中的嵌套解构
4. 解构的应用场景
解构赋值非常适合用在以下场景:
5. 解构更新
在实际开发中,当我们希望更新对象的某些字段而不修改原对象时,可以用解构创建一个新对象,将更新内容解构到新对象中。通过解构来更新对象,是一种符合不可变数据原则的方法。
例如,假设我们有一个对象
person
,需要更新其中的name
字段。示例:对象解构更新
解释
更新多个字段
我们可以在解构更新中覆盖多个字段:
2. 数组解构更新
数组解构更新比较常见的场景是添加、删除或替换特定元素。数组不像对象那样可以通过字段名直接覆盖,我们需要通过构造新的数组来进行更新。
示例:数组解构更新
解构更新的总结
基础样式控制
React组件基础的样式控制有两种方案:
1. 内联样式(或者行内样式)
内联样式是将样式直接应用于React组件的元素中。这种方式通过
style
属性实现,样式对象的属性名为驼峰命名法,值为字符串。示例
优点
- 动态样式:内联样式允许根据组件的状态动态计算样式值。
- 封装性:样式与组件逻辑在一起,便于管理。
缺点
- 缺乏伪类和媒体查询:内联样式不能使用CSS伪类(如
:hover
)和媒体查询。
- 可读性差:样式与结构混在一起,可能导致代码可读性下降。
2. 外部样式表(CSS)
外部样式表是将样式定义在单独的CSS文件中,通过
className
或id
属性将样式应用到React组件的元素。示例
优点
- 分离关注点:将样式与组件逻辑分开,提高了代码的可维护性。
- 更丰富的样式功能:可以使用伪类、媒体查询等CSS功能。
缺点
- 全局命名冲突:样式可能与其他样式冲突,需要注意命名。
- 缺乏动态控制:无法轻易根据状态变化动态调整样式(需要额外逻辑处理)。
小结
在React中,内联样式和外部样式表各有优缺点,选择哪种方案取决于具体的使用场景:
- 内联样式适合于需要动态更新样式的场景。
- 外部样式表适合于样式复杂且需要复用的场景。
受控表单绑定
概念:使用React组件的状态(useState)控制表单的状态。在React中,受控组件指的是那些将表单的值存储在组件的状态中,并且通过状态更新来改变表单值的组件。受控组件的值完全由组件的状态控制,通过React的
state
管理表单的输入值。通常我们会将输入框的值绑定到组件的状态上,并在每次输入变化时更新状态。React中的受控组件包括各种表单元素,如文本框、复选框、单选按钮、下拉菜单等
受控组件的实现步骤
以文本输入框为例,来实现一个受控组件:
- 创建状态来存储表单的值。
const [value,setValue] = useState('')
- 使用
value
属性将状态绑定到输入框。
- 使用
onChange
事件处理器,在输入框内容变化时更新状态。
<input type="text" value={value} onChange={(e)=> setValue(e.target.value)}/>
示例:文本输入框的受控组件
在这个例子中:
name
状态是输入框的值来源。
value={name}
确保了输入框的值总是与name
状态同步。
onChange
事件每次触发都会更新name
状态,这样输入框的值会实时反映在页面上。
受控组件的优点
- 数据同步:输入框的值与状态同步,任何地方访问
name
状态就可以得到最新的输入值。
- 便于验证:可以在
onChange
中添加数据校验、格式化等逻辑。
- 更易管理表单数据:在提交表单时,数据已经存在组件的状态中,无需额外处理。
示例:多输入框的受控表单
如果有多个输入框,可以使用多个状态,也可以用一个对象来管理。
总结
- 受控组件通过状态绑定和事件处理实现对表单输入的实时控制。
- 状态同步确保了表单输入的值始终和组件状态一致,便于数据操作和验证。
- 在多输入框场景中,使用对象来存储多个输入框的值会更加简洁
React获取Dom
在React中,获取和操作原生DOM元素通常通过**
ref
**(引用)来实现。ref
允许我们直接访问某个DOM节点或React组件的实例,以便在特定场景下直接操作DOM,比如设置焦点、获取宽高等1. 使用useRef
获取DOM元素(适用于函数组件) hooks函数
在React的函数组件中,使用
useRef
来创建一个ref
对象,并将它绑定到DOM元素上,之后就可以通过这个ref
对象访问该DOM元素。两个步骤:
1、使用useRef创建ref对象,并与JSX绑定
const inputRef = useRef(null)
<input type="text" ref={inputRef} />
2、在DOM可用时,通过inputRef.current
拿到DOM对象console.log(input.current)
示例:获取输入框并设置焦点
在这个例子中:
useRef
初始化了一个ref
对象inputRef
,null
表示初始值。
- 将
ref
对象绑定到<input ref={inputRef}>
元素上。
inputRef.current
访问<input>
元素,通过focus()
方法设置焦点
2. 使用React.createRef
获取DOM元素(适用于类组件)
在类组件中,可以使用
React.createRef
创建ref
对象,然后将它附加到组件内的DOM元素上,以实现对该元素的直接操作。示例:类组件获取输入框并设置焦点
在这里,
this.inputRef
是通过React.createRef()
创建的。在render
方法中,将其传递给<input>
的ref
属性,this.inputRef.current
就能访问<input>
元素。在现代React开发中,函数组件配合
useRef
使用最为常见。这主要是因为以下几个原因:- 函数组件已成为主流:React社区和官方的推荐趋势是使用函数组件配合
Hooks
,因为它们代码简洁,逻辑更容易复用,且减少了生命周期管理的复杂性。
useRef
简单且灵活:在函数组件中,useRef
提供了一个直接访问DOM节点的方式,而且不会在每次渲染时改变引用(即不会触发重渲染),因此性能较优。
- 渐少使用类组件:随着React Hooks的引入,类组件在新项目中已不常用,因此
React.createRef
的使用频率也随之降低。
总结
- 如果是函数组件,使用
useRef
是最佳实践。
- 如果是旧代码或必须使用类组件的场景,则可以使用
React.createRef
。
组件通信
概念
组件通信就是组件之间的数据传递,根据组件嵌套关系的不同,有不同的通信方法。
于React组件层级结构的特性,组件之间的通信分为以下几种典型的场景:
- 父组件到子组件通信(props传递)
- 子组件到父组件通信(回调函数)
- 兄弟组件之间的通信(状态提升、Context、全局状态管理)
- 跨层级组件的通信(Context、全局状态管理)
父传子——基础实现
实现步骤:
- 父组件传递数据:在子组件标签上绑定属性
- 子组件接受数据:子组件通过props参数接收数据
props可传递任意的数据
数字、字符串、布尔值、数组、对象、函数、JSX。
使用解构简化对props的使用
在这个例子中,我们直接解构了
props
对象,将name
提取出来使用,简化了props.name
的写法props是只读对象
子组件只能读取props中的数据,不能直接进行修改,父组件的数据只能由父组件修改
props中children属性
在React中,
children
是一个特殊的props
属性,允许父组件将嵌套在组件标签中的内容传递给子组件。这种方式在构建可复用组件时非常实用,比如用于包装内容的布局组件、卡片组件等。children
的工作原理当你在父组件中使用组件标签时,可以在标签内部嵌套任何内容(例如文本、HTML结构、其他组件等)。React会自动将这些嵌套内容作为
children
属性传递给子组件,子组件可以通过props.children
访问和使用这些内容。示例:基础用法
在这个例子中:
- 父组件
Parent
在<Child>
标签内部嵌套了一个<p>
元素。
Child
组件通过{children}
渲染这些嵌套内容。
children
允许父组件在不修改子组件代码的前提下控制其显示内容。
子传父
核心思路:在子组件调用父组件中的函数并传参。
在React中,子组件传递数据到父组件的常见方法是通过回调函数。父组件可以将一个回调函数作为
props
传递给子组件,然后子组件在特定操作(例如点击、输入等)时调用该函数,将数据传回父组件。子传父的基本实现方式
- 在父组件中定义回调函数,这个函数会接收子组件传来的数据并进行处理(如更新状态)。
- 将回调函数作为
props
传递给子组件。
- 子组件在需要时调用回调函数,并传入相应的数据。
示例:通过回调函数实现子传父
以下是一个简单的例子,演示如何通过回调函数实现子组件向父组件传递数据:
在这个例子中:
- 父组件
Parent
定义了一个handleReceiveMessage
回调函数,该函数接收参数并更新父组件的message
状态。
- 父组件将
handleReceiveMessage
函数通过onMessageSend
属性传递给子组件。
- 子组件
Child
通过点击按钮,调用onMessageSend
回调函数,将字符串"Hello from Child"
传递回父组件。
- 父组件接收到子组件的数据后,更新
message
状态并显示。
另一种示例:子组件传递输入值到父组件
通过这种方式,子组件可以将用户输入的数据实时传递给父组件。
在这个例子中:
- 子组件的
<input>
在输入值改变时会调用handleChange
函数,handleChange
函数通过onInputChange
回调将当前输入值传递给父组件。
- 父组件通过回调函数
handleInputChange
接收输入值并更新inputValue
状态,从而实现实时展示子组件的输入内容。
总结
- 父组件定义回调函数:接收数据并进行处理。
- 通过
props
将回调函数传递给子组件。
- 子组件在事件中调用回调函数,将数据传递给父组件。
状态提升实现兄弟组件通信
借助“状态提升”机制,通过父组件进行兄弟组件之间的数据传递。
- A组件先通过子传父的方式把数据传给父组件App。
- App拿到数据后通过父传子的方式再传给B组件。
使用context机制跨层级组件通信
实现步骤:
- 使用createContext方法创建一个上下文对象Ctx
- 在顶层组件(App)中通过Ctx.provider组件提供数据
- 在底层组件(B)中通过useContext钩子函数获取消费数据。
useEffect
useEffect是一个React Hook函数,用于在React组件中创建不是由事件引起而是由渲染本身引起的操作(副作用),比如发送AJAX请求,更改DOM等等。用于在函数组件中处理副作用。副作用通常包括数据获取、订阅事件、手动更改DOM,以及在组件生命周期中执行清理工作等。这些副作用原来只能在类组件的生命周期方法(如
componentDidMount
、componentDidUpdate
、componentWillUnmount
等)中处理,但通过useEffect
,我们可以在函数组件中轻松管理这些逻辑。说明:上面的组件中没有发生任何的用户事件,组件渲染完毕之后就需要和服务器要数据,整个过程属于 “只由渲染引起的操作”。
useEffect
基本语法- 第一个参数是一个回调函数,其中包含我们要执行的副作用逻辑。
- 第二个参数是一个可选的依赖数组,用于控制
useEffect
的调用频率。 - 无依赖的
useEffect
:仅在组件首次渲染时执行。 - 空数组
[]
表示useEffect
只会在组件首次渲染时执行一次。 - 带依赖的
useEffect
:依赖变化时执行。 - 当
name
值发生变化时,useEffect
会重新执行。 - 无依赖数组的
useEffect
:在每次渲染后都执行。 - 不提供依赖数组时,
useEffect
会在每次组件渲染后执行。 - 清理副作用:在组件卸载或依赖项更改时清理。
- 当组件卸载时(或依赖项更改时),
useEffect
会调用返回的清理函数,以避免内存泄漏或重复订阅。
依赖项 | 副作用函数执行时机 |
没有依赖项 | 数组初始渲染+组件更新时执行 |
空数组依赖 | 只在初始渲染时执行一次 |
添加特定依赖项 | 组件初始渲染 + 特性依赖项变化时执行 |
使用场景
在useEffect中编写的由渲染本身引起的对接组件外部的操作,社区也经常把它叫做副作用操作,比如在useEffect中开启了一个定时器,我们想在组件卸载时把这个定时器再清理掉,这个过程就是清理副作用。
说明:清除副作用的函数最常见的执行时机是在组件卸载时自动执行。
示例:获取数据并处理依赖项
以下示例中,
useEffect
用于组件加载时获取数据,并在userId
更改时重新获取数据。在这个例子中:
useEffect
依赖userId
,当userId
发生变化时,会重新获取用户数据。
fetchData
函数只在userId
变化时执行。
假设我们从API获取一组用户数据并在组件中渲染:
执行顺序解析
- 初次渲染:
useState([])
初始化data
为一个空数组。- React执行组件的初次渲染,渲染出标题“用户列表”和一个空的
<ul>
列表。 - 初次渲染完成后,
useEffect
的回调函数执行。
- 执行
useEffect
中的异步请求: useEffect
在初次渲染后立即执行。fetchData
函数通过API请求数据,当数据返回时,调用setData(result)
更新data
状态。setData
会触发组件重新渲染。
- 组件重新渲染:
data
状态更新为新获取的数据后,React会重新渲染组件。- 在
return
语句中,React使用新的data
值渲染出<ul>
中的每一项。
渲染和函数执行顺序总结
- 初次渲染:先渲染空的数据结构,再执行
useEffect
中的副作用。
- 副作用执行:在副作用中执行异步数据请求,数据请求完成后更新状态。
- 再次渲染:
data
状态更新触发重新渲染,将获取到的数据正确渲染在页面上。
注意事项
- 异步函数在
useEffect
中的用法:我们通常会在useEffect
中定义并调用异步函数,而不是直接将async
关键字放在useEffect
回调函数上。
- 空依赖数组:确保
useEffect
仅在组件初次加载时执行一次,避免不必要的重复请求。
- 键值唯一性:在
.map()
遍历渲染时使用user.id
作为key
,确保每个子元素在DOM中具有唯一性。
关于副作用
useEffect
可以理解为给组件添加一些“副作用”的机制。它的主要作用就是在组件完成渲染之后,执行一些在渲染过程中之外的操作,比如数据请求、事件监听、DOM操作等。为什么叫“添加副作用”
useEffect
的“副作用”作用于组件渲染之外,虽然这些操作并不会直接影响组件的核心渲染流程,但它们可以在特定时刻触发额外的行为。可以说,useEffect
的本质就是让你在组件内安全地添加一些额外的行为,而这些行为可以根据组件的生命周期或状态变化来触发。副作用简单来说就是:组件在渲染(显示)内容之外还需要做的额外操作,比如:
- 数据获取:从API请求数据,这种操作不直接影响页面显示本身,但获取到的数据会影响显示内容。
- 事件订阅:在某些情况下,组件需要监听事件(如鼠标点击、窗口大小变化)并响应。
- 手动修改DOM:例如向文档中添加、修改内容,这些操作影响页面结构或内容。
- 设置定时器:比如
setTimeout
或setInterval
等,影响页面状态。
这些操作都会产生副作用,因为它们不仅仅依赖组件的初始数据,还会导致组件外的环境发生变化或者依赖外部的数据资源。
- 例如:给组件添加一些副作用
假设我们想要在页面加载时请求一个API数据,这个数据会影响组件的显示内容。此时可以用
useEffect
来在组件加载后发起请求,然后更新状态:在这个例子里:
useEffect
中包含了数据请求的逻辑。
- 这个逻辑不属于直接的渲染逻辑,而是渲染完成后需要执行的额外操作。
useEffect
执行的数据请求就是一个“副作用”,而useEffect
让我们能够优雅地管理这个副作用。
- 示例:
count
变化时触发useEffect
假设我们有一个计数器,
count
每次更新时都会执行useEffect
,在浏览器的localStorage
中保存这个count
值:在这个例子中:
- 初次渲染时:组件渲染
<p>Count: 0</p>
,然后useEffect
运行一次,把count=0
保存到localStorage
。
- 点击按钮时:
setCount(count + 1)
会让count
变为1
,触发组件重新渲染。
- 重新渲染后:
useEffect
检测到count
变化,执行保存操作,把count=1
存入localStorage
。
因此,每次
count
更新都会触发渲染,紧接着会执行useEffect
的副作用。对比vue中的生命周期函数
在React中,确实存在与Vue类似的组件生命周期,只不过表现方式不同。在React中,组件的生命周期可以通过类组件的生命周期方法和函数组件中的
useEffect
钩子来管理。1. 组件挂载(相当于
componentDidMount
)如果想在组件第一次挂载后执行某些逻辑(例如数据获取、事件监听等),可以传递一个空依赖数组
[]
给useEffect
:2. 组件更新(相当于
componentDidUpdate
)如果你需要在特定的状态或属性变化时执行某些逻辑,可以将这些依赖项放入
useEffect
的依赖数组中。例如,当某个状态(如count
)变化时执行:3. 组件卸载(相当于
componentWillUnmount
)要在组件卸载前执行一些清理工作,可以在
useEffect
中返回一个清理函数。这个函数会在组件卸载时自动调用,类似于componentWillUnmount
的效果:小结
通过
useEffect
,函数组件可以处理挂载、更新和卸载等生命周期逻辑,使得函数组件能够更加灵活和简洁地管理副作用React中的生命周期
在React中,组件的生命周期分为三个主要阶段:挂载、更新和卸载。下面是每个阶段的具体说明和触发条件:
1. 组件挂载
什么是挂载?
- 挂载是指组件被创建并插入到DOM中的过程。在这个阶段,React会调用相关的方法来初始化组件。
什么时候发生挂载?
- 当组件第一次被渲染到页面上时会发生挂载。具体情况包括:
- 在父组件中使用子组件,React会创建子组件的实例并将其添加到DOM中。
- 在应用加载时,根组件会挂载并渲染其子组件。
对应的方法(类组件):
constructor
:初始化状态和绑定方法。
componentDidMount
:组件挂载后调用,可以进行数据请求、订阅等操作。
函数组件:
- 使用
useEffect(() => {}, [])
:在组件首次挂载后执行。
2. 组件更新
什么是更新?
- 更新是指组件的状态(state)或属性(props)发生变化时,React会重新渲染组件的过程。
组件更新的过程确实包含了组件挂载的概念,但两者是不同的阶段.
挂载(Mounting)
- 定义:挂载是指组件首次被创建并添加到DOM中的过程。这个阶段发生在组件的生命周期开始时。
- 发生情况:组件在首次渲染时,React调用挂载相关的生命周期方法。
- 对应的方法:
- 对于类组件,
constructor
和componentDidMount
会被调用。 - 对于函数组件,
useEffect
会在首次挂载时执行。
更新(Updating)
- 定义:更新是指组件的状态(state)或属性(props)发生变化时,导致组件重新渲染的过程。
- 发生情况:当组件的状态或属性变化时,React会重新渲染该组件,这个过程可以被看作是“更新”。
- 包含的情况:更新不仅包括已经挂载的组件(即已存在于DOM中的组件),也包括组件因状态或属性变化而重新渲染的情况。第一次渲染(挂载)也是一种更新,因为组件从未存在于DOM中。
- 对应的方法:
- 对于类组件,
componentDidUpdate
会被调用。 - 对于函数组件,依赖于
useEffect
的变化来执行逻辑。
总结
- 挂载是更新的一部分。更新包括:
- 组件的首次挂载(此时状态和属性为初始值)。
- 后续的状态或属性变化导致的重新渲染。
所以,尽管“更新”这个术语通常指的是状态或属性变化引起的变化,但它也涵盖了组件首次挂载时的情况。通过这种理解,开发者可以更清楚地管理组件的生命周期和状态变化。
什么时候发生更新?
- 组件的更新可以由以下几种情况触发:
- 更新状态:使用
setState
或useState
修改组件的状态。 - 更新属性:父组件传递给子组件的新props。
- 强制更新:使用
forceUpdate
方法(不常用,通常不推荐)。
对应的方法(类组件):
componentDidUpdate
:组件更新后调用,可以比较新旧状态或属性。
shouldComponentUpdate
:决定组件是否需要更新,返回true
或false
。
函数组件:
- 使用
useEffect(() => {}, [dependencies])
:当依赖项变化时执行。
3. 组件卸载
什么是卸载?
- 卸载是指组件从DOM中移除的过程。在这个阶段,React会清理组件的资源,防止内存泄漏。
什么时候发生卸载?
- 组件被移除时会发生卸载,通常情况包括:
- 当父组件重新渲染并不再包含子组件。
- 在路由切换时,组件被卸载。
- 当条件渲染使得组件不再渲染时。
对应的方法(类组件):
componentWillUnmount
:在组件卸载前调用,用于清理订阅、定时器等副作用。
函数组件:
- 在
useEffect
的返回函数中进行清理,这个函数会在组件卸载时被调用。
小结
- 挂载:当组件首次渲染到DOM中时,调用
componentDidMount
(类组件)或useEffect
(函数组件)中的初始逻辑。
- 更新:当组件的状态或属性发生变化时,调用
componentDidUpdate
(类组件)或相应的useEffect
(函数组件)。
- 卸载:当组件从DOM中移除时,调用
componentWillUnmount
(类组件)或useEffect
的清理函数(函数组件)。
总结
useEffect
允许你在组件渲染之外添加额外的行为,并确保这些行为在组件的整个生命周期中得到妥善的管理和清理。这种“添加副作用”的机制让React函数组件不仅能够完成显示内容,还能灵活地与外部环境进行交互。总结
useEffect
使函数组件能够在不同渲染阶段处理副作用。
- 通过设置依赖项,可以控制
useEffect
何时执行。
- 清理函数可以防止组件卸载或依赖变化时的资源泄漏。
自定义hook
自定义Hook是以use打头的函数,通过自定义Hook函数可以用来实现逻辑的封装和复用。
通过自定义 Hook,开发者可以在多个组件之间共享状态逻辑,而无需改变组件的结构。
什么是自定义 Hook?
自定义 Hook 是以
use
开头的普通 JavaScript 函数,内部可以使用其他 Hook,例如 useState
、useEffect
等。自定义 Hook 允许将状态逻辑提取到函数中,从而在多个组件中重用。自定义 Hook 的步骤
- 定义函数:创建一个以
use
开头的函数。
- 使用内置 Hook:在该函数内部使用 React 的内置 Hook。
- 返回值:返回需要共享的状态和行为。
示例
假设我们需要一个计数器的自定义 Hook,可以在多个组件中使用。
何时需要自定义 Hook?
- 逻辑复用:当多个组件需要共享相似的状态逻辑时,使用自定义 Hook 可以减少重复代码。
- 复杂逻辑管理:当某个组件的状态逻辑过于复杂,导致
useEffect
和useState
嵌套过深时,可以考虑将其提取为自定义 Hook。
- 状态管理:处理复杂的状态管理时(例如,表单处理、异步请求等)。
注意事项
- 命名规则:自定义 Hook 必须以
use
开头,以便 React 能识别它并保证其遵循 Hook 的规则。
- 避免副作用:在自定义 Hook 中,确保副作用清晰,并在必要时进行清理。
- 保持简单:尽量保持自定义 Hook 简单易懂,避免过度复杂化。
- 只能在组件中或者其他自定义Hook函数中调用
- 只能在组件的顶层调用,不能嵌套在if、for、其他函数中。
理解:就是Hooks不能有条件的执行,只能直接在顶部采用 const {value,handleToggle} = useToggle( ) 这种方式,直接调用。
最佳实践
- 保持可重用性:设计自定义 Hook 时,确保它足够通用,以便在多个地方使用。
- 清晰的接口:自定义 Hook 应该有明确的输入(参数)和输出(返回值),使其易于使用。
- 文档化:为自定义 Hook 提供使用示例和文档,便于团队成员理解和使用。
- 组合 Hooks:可以将多个自定义 Hook 组合在一起,以实现更复杂的功能。
和共用函数的区别
在 React 中,普通函数是不能直接使用
useState
、useEffect
这样的 Hook 的Hook 的使用规则
- 在函数组件或自定义 Hook 中调用:
- Hook 只能在函数组件的顶层调用,或者在自定义 Hook 中调用。也就是说,不能在常规的普通函数中调用 Hook。
- React 通过这种方式确保了 Hook 的调用顺序是相同的,从而使 React 可以正确地管理组件的状态和生命周期。
- 不能在条件语句、循环或嵌套函数中调用 Hook:
- 这同样是为了保证 Hook 的调用顺序不变。否则,React 无法正确追踪状态和副作用的管理。
为什么不能在普通函数中使用 Hook?
- 状态管理:
useState
和useEffect
是设计用来管理组件状态和副作用的。它们依赖于 React 的内部机制,只有在 React 管理的环境中才能正常工作。
- 生命周期:React 组件有明确的生命周期(如挂载、更新和卸载),而普通函数没有这样的生命周期。Hook 的实现依赖于这些生命周期。
B站评论案例
需求:
渲染评论列表
- 使用useState维护评论列表
- 使用map方法对列表数据进行遍历渲染(别忘了加key)
删除评论
- 只有自己的评论才显示删除按钮
- 点击删除按钮,删除当前评论,列表中不再显示
渲染导航tab和高亮显示
点击谁就把谁的type(独一无二的标识)记录下来,然后和遍历时的每一项的type做匹配,谁匹配到就设置负责高亮的类名。
评论列表排序功能
- 点击最新,评论列表按照创建时间排序(新的在前),点击最热按照点赞数排序(多的在前)
- 把评论列表状态数据进行不同的排序处理,当成新值传给set函数重新渲染视图UI。
lodash
Lodash 是一个一致性、模块化、高性能的 JavaScript 实用工具库。
‣
classnames优化类名控制
classnames是一个简单的JS库,可以非常方便的通过条件控制class类名的显示。
发表评论
- 获取评论内容
- 点击发布按钮——发布评论
1、rpid要求一个唯一的随机数 id-uuid(uuid这个库)
2、ctime要求以当前时间为标准,生成固定的格式 (day.js)
3、清空内容并重新聚焦
优化需求
通过接口获取评论列表并渲染
使用json-server 工具模拟接口服务,通过axios 发送接口请求。
注:json-server是一个快速以.json文件作为数据源模拟接口服务的工具。axios是一个广泛使用的前端请求库。
json-server的使用
使用useEffect调用接口获取数据
自定义hook函数封装数据请求
封装一个可以执行数据请求的自定义 Hook,并将请求状态和数据返回给组件使用。
思路步骤
1. 编写一个
use
开头的函数所有的自定义 Hook 都应该以
use
开头(例如 useFetch
),这是 React 识别该函数为 Hook 的方式。使用 use
前缀还可以帮助开发者知道这个函数包含了可能影响 React 渲染的状态逻辑。2. 函数内部编写封装的逻辑
在自定义 Hook 中封装数据请求的核心逻辑,包括:
- 定义状态:用于存储请求数据、请求状态(如加载中)和错误信息。
- 使用
useEffect
:useEffect
Hook 可以在组件挂载时触发请求。
- 异步请求:在
useEffect
中进行异步请求,并根据请求的结果更新状态。
3.
return
出去组件中用到的状态和方法将请求的数据、加载状态和错误信息返回给调用组件,以便组件能够访问这些值。
4. 组件中调用 Hook 并解构赋值使用
在组件中调用自定义 Hook,然后将其返回的状态和数据解构赋值使用,展示数据或控制加载、错误状态。
详细代码实现
步骤 1 和 2:创建
useFetch
自定义 Hook步骤 3:在组件中调用
useFetch
Hook使用这个自定义 Hook,可以使组件代码更简洁,并集中管理数据请求的逻辑。
步骤 4:在组件中解构赋值和使用
封装评论项Item组件
抽象原则:App作为"智能组件"负责数据的获取,Item作为"UI组件"负责数据的渲染
在 React 代码设计中,“智能组件”和 “UI 组件” 是两种常见的组件分类方式,用于帮助开发者更好地分离关注点、提高代码的可复用性和可维护性。这种划分方式受到"容器组件"和"展示组件"设计模式的影响,尤其是在管理复杂的状态和逻辑时非常实用。
智能组件(Smart Component)
智能组件,顾名思义,负责“思考”——它们处理数据、业务逻辑和状态管理。智能组件常常是“容器组件”,负责连接数据源(比如后端 API 或 Redux 的全局状态),并将数据传递给子组件。它们通常不包含样式,专注于“如何获取和管理数据”。
特点:
- 负责状态和逻辑:通常包含
useState
、useEffect
等状态和副作用逻辑。
- 与数据源交互:可以发起 API 请求或从全局状态(如 Redux)中获取数据。
- 向子组件传递数据和回调函数:把处理好的数据和方法传递给子组件(即 UI 组件)。
- 不关心样式和布局:专注于逻辑,而不直接管理 UI 样式。
示例:
在这个例子中,
UserContainer
是一个智能组件,负责获取用户数据并将数据传递给 UserList
。它不负责呈现具体的 UI 样式。UI 组件(Dumb Component)
UI 组件,也称为“展示组件”,仅负责显示内容。它们往往只关心接收的 props,并根据这些 props 来呈现 UI,通常不包含状态管理逻辑或数据获取逻辑。UI 组件关注于“如何展示数据”,并包含样式和布局。
特点:
- 只关注 UI:通常是无状态的函数组件,仅负责根据 props 渲染内容。
- 接收并展示数据:通过 props 接收数据并在 UI 中显示。
- 无数据源交互:不包含与外部数据源的交互。
- 高可复用性:因为没有业务逻辑或状态依赖,可以在多个场景下复用。
示例:
在这个例子中,
UserList
是一个 UI 组件,它从父组件接收 users
和 loading
两个 props,根据这些 props 渲染内容。它不涉及数据获取或状态管理逻辑。智能组件和 UI 组件的优势
这种划分方式能够帮助开发者清晰地分离逻辑代码和展示代码,带来以下好处:
- 提高组件的可复用性:UI 组件没有逻辑依赖,能在不同场景下复用。
- 降低复杂度:将业务逻辑和 UI 分离,使得每个组件职责单一、易于测试和维护。
- 增强测试性:UI 组件由于没有状态,更容易测试,而智能组件的逻辑可以独立测试。
什么时候使用智能组件和 UI 组件
- 智能组件:当一个组件需要处理数据、状态、API 请求或其他复杂逻辑时,考虑将其设计为智能组件。
- UI 组件:当一个组件只负责展示内容而不需要管理数据时,优先将其设计为 UI 组件。
总结
智能组件和 UI 组件的划分是组件设计的一种思路。智能组件处理数据和逻辑,将数据传递给 UI 组件,而 UI 组件专注于展示。这样的分层设计可以有效提升代码的结构清晰度、复用性和可维护性,是 React 中常用的设计模式。
改造后的结果
- 作者:coderma4k
- 链接:https://coderma4k.com//article/128bd2a3-ceeb-80a7-b8ac-f40dedfd80c3
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。