| |
|
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
| -> JavaScript知识库 -> React实战精讲(React_TS/API) -> 正文阅读 |
|
|
[JavaScript知识库]React实战精讲(React_TS/API) |
大家好,我是柒八九。 今天,我们继续前端面试的知识点。我们来谈谈关于React实战的相关知识点和具体的算法。 该系列的文章,大部分都是前面文章的知识点汇总,如果想具体了解相关内容,请移步相关系列,进行探讨。 如果,想了解该系列的文章,可以参考我们已经发布的文章。如下是往期文章。 文章list好了,天不早了,干点正事哇。 你能所学到的知识点
TS_React:使用泛型来改善类型
|
TypeScript | JavaScript |
|---|---|
| JavaScript 的超集 ?于解决?型项?的代码复杂性 | ?种脚本语? ?于创建动态?? |
| 可以在编译期间发现并纠正错误 | 作为?种解释型语?,只能在运?时发现错误 |
| 强类型,?持静态和动态类型 | 弱类型,没有静态类型选项 |
| 最终被编译成 JavaScript 代码,使浏览器可以理解 | 可以直接在浏览器中使? |
| ?持模块、泛型和接? | 不?持泛型或接? |

在上图中包含 3 个 ts ?件:a.ts、b.ts 和 c.ts。这些?件将被 TypeScript 编译器,根据配置的编译选项编译成 3 个 js ?件,即 a.js、b.js 和 c.js。对于?多数使? TypeScript 开发的 Web 项?,我们还会对编译?成的 js ?件进?打包处理,然后在进?部署。
TypeScript 主要有 3 大特点:
TypeScript 可以编译出纯净、 简洁的 JavaScript 代码,并且可以运行在任何浏览器上、Node.js 环境中和任何支持 ECMAScript 3(或更高版本)的JavaScript 引擎中。JavaScript 开发者在开发 JavaScript 应用程序时使用高效的开发工具和常用操作比如静态检查和代码重构。TypeScript 提供最新的和不断发展的 JavaScript 特性,包括那些来自 2015 年的 ECMAScript 和未来的提案中的特性,比如异步功能和 Decorators,以帮助建立健壮的组件。泛型指的是类型参数化:即将原来某种具体的类型进?参数化
在像 C++/Java/Rust 这样的 OOP 语?中,可以使?泛型来创建可重?的组件,?个组件可以?持多种类型的数据。 这样?户就可以以??的数据类型来使?组件。
设计泛型的关键?的是在成员之间提供有意义的约束,这些成员可以是:类的实例成员、类的?法、函数参数和函数返回值。
TypeScript类型与 JavaScript对象进行比较。
主要的区别是
- 在
JavaScript中,关心的是变量的值- 在
TypeScript中,关心的是变量的类型
但对于我们的User例子来说,使用一个泛型看起来是这样的。
// `User` 现在是泛型类型
const user: User<'在线' | '离线'>;
// 我们可以手动新增一个新的类型 (空闲)
const user: User<'在线' | '离线' | '空闲'>;
上面说的是 user变量是类型为User的对象。
我们继续来实现这个类型。
// 定义一个泛型类型
type User<StatusOptions> = {
name: string;
status: StatusOptions;
};
StatusOptions 被称为 {类型变量| type variable},而 User 被说成是 {泛型类型|generic type }。
通常的情况是,当你想让一个类型在多个实例中共享,而每个实例都有一些不同:即这个类型是动态的。
?先我们来定义?个通?的 identity 函数,函数的返回值的类型与它的参数相同
我们的?标是让 identity 函数可以适?于任何特定的类型,为了实现这个?标,我们可以使?泛型来解决这个问题,具体实现?式如下:
function identity <T>(value: T) : T {
return value;
}
console.log(identity<Number>(1)) // 1
看到 <T> 语法,就像传递参数?样,上面代码传递了我们想要?于特定函数调?的类型。

也可以引?希望定义的任何数量的类型变量。?如我们引??个新的类型变量 U ,?于扩展我们定义的 identity 函数:
function identity <T, U>(value: T, message: U) : T {
console.log(message);
return value;
}
console.log(identity<Number, string>(68, "TS真的香喷喷"));

有时我们可能希望限制每个类型变量接受的类型数量,这就是泛型约束的作?。下?我们来举?个例?,介绍?下如何使?泛型约束。
有时候,我们希望类型变量对应的类型上存在某些属性。这时,除?我们显式地将特定属性定义为类型变量,否则编译器不会知道它们的存在。
例如在处理字符串或数组时,我们会假设 length 属性是可?的。让我们再次使? identity 函数并尝试输出参数的?度:
function identity<T>(arg: T): T {
console.log(arg.length); // Error
return arg;
}
在这种情况下,编译器将不会知道 T 确实含有 length 属性,尤其是在可以将任何类型赋给类型变量 T 的情况下。我们需要做的就是让类型变量 extends ?个含有我们所需属性的接?,?如这样:
interface Length {
length: number;
}
function identity<T extends Length>(arg: T): T {
console.log(arg.length); // 可以获取length属性
return arg;
}
T extends Length ?于告诉编译器,我们?持已经实现 Length 接?的任何类型。
在前面的例子中,我们只举例了如何用泛型定义常规的函数语法,而不是ES6中引入的箭头函数语法。
// ES6的箭头函数语法
const identity = (arg) => {
return arg;
};
如果想要在处理箭头函数,需要使用下面的语法。
// 方式1
const identity = <ArgType,>(arg: ArgType): ArgType => {
return arg;
};
// 方式2
const identity = <ArgType extends unknown>(arg: ArgType): ArgType => {
return arg;
};
出现上述问题的根源在于:这是TSX(TypeScript+ JSX)的特定语法。在正常的 TypeScript 中,不需要使用这种变通方法。
在绝大部分,
TS都可以根据hook中的值来推断它们的类型:也就是我们常说的类型推断
何为类型推断,简单来说:类型推断就是基于赋值表达式推断类型的能?。ts采用将类型标注声明放在变量之后(即类型后置)的方式来对变量的类型进行标注。而使?类型标注后置的好处就是编译器可以通过代码所在的上下?推导其对应的类型,?须再声明变量类型。
像
都可以根据上下?推断出来。
例如,下面的代码可以在ts环境中正常运行,且能够通过类型推断推导出name的类型为string类型。
const [name, setName] = useState('前端柒八九');
下面的两种情境下,类型推断有点力不从心
ts推断出的类型过于宽松我们之前的例子–有一个字符串类型的name。但是我们假设这个name只能有两个预定的值中的一个。
在这种情况下,我们会希望name有一个非常具体的类型,例如这个类型。
type Name = '前端柒八九' | '前端工程师' ;
这种类型同时使用联合类型和字面类型。
在这种情况下,推断的类型过于宽松(是string,而不是我们想要的2个字符串的特定子集),这种情况下就必须自己指定类型。
const [name, setName] = useState<Name>('前端柒八九');
有时,推断的类型是错误的(或者限制性太强不是你想要的类型)。这种情况经常发生在React的useState 默认值中。比方说,name 的初始值是null。
const [name, setName] = useState(null);
在这种情况下,TypeScript 会推断出name是null类型的(这意味着它总是null)。这显然是错误的:我们以后会想把 name 设置成一个字符串。
此时你必须告诉 TypeScript,它可以是别的类型。
const [name, setName] = useState<string | null>(null);
通过这样处理后,TypeScript 会正确理解name可以是null也可以是string。
这里要提到的一件事是,当类型推断不起作用时,应该依靠泛型参数而不是类型断言。
const [name, setName] = useState<Name>('前端柒八九');推荐使用const [name, setName] = useState('前端柒八九' as Name);不推荐使用
在前面,我们已经通过类型推断讲过了,如何处理useState的各种情况。这里就不在赘述了。
const [name, setName] = useState<string | null>(null);
useReducer 的类型比 useState 要复杂一些。其实useState就是useReducer的简化版。
针对useReducer有两样东西要类型化处理:state和action。
这里有一个useReducer的简单例子。针对input做简单的数据收集处理。
import { useReducer } from 'react';
const initialValue = {
username: '',
email: '',
};
const reducer = (state, action) => {
switch (action.type) {
case 'username':
return { ...state, username: action.payload };
case 'email':
return { ...state, email: action.payload };
case 'reset':
return initialValue;
default:
throw new Error(`未定义的action: ${action.type}`);
}
};
const Form = () => {
const [state, dispatch] = useReducer(reducer, initialValue);
return (
<div>
//....
</div>
);
};
export default Form;
我们有两个选择来类型化reducer-state。
typeof 操作符const initialValue = {
username: '',
email: '',
};
+ const reducer = (state: typeof initialValue, action) => {
///....
};
+type State = {
+ username: string;
+ email: string;
+};
+ const reducer = (state: State, action) => {
// ....
};
reducer-action的类型比reducer-state要难一点,因为它的结构会根据具体的action而改变。
例如,对于 username-action,我们可能期望有以下类型。
type UsernameAction = {
type: 'username';
payload: string;
};
但对于 reset-action,我们不需要payload字段。
type ResetAction = {
type: 'reset';
};
我们可以借助联合类型区别对待不同的action。
const initialValue = {
username: "",
email: ""
};
+type Action =
+ | { type: "username"; payload: string }
+ | { type: "email"; payload: string }
+ | { type: "reset" };
+ const reducer = (state: typeof initialValue, action: Action) => {
//....
};
Action类型表示的是,它可以接受联合类型中包含的三种类型中的任何一种。因此,如果 TypeScript 看到 action.type是username,它就会自动知道它应该是第一种情况,并且payload应该是一个string。
通过对
state/action类型化后,useReducer能够从reducer函数的type中推断出它需要的一切。
useRef 有两个主要用途
它基本上与 useState 相同。想让useRef保存一个自定义的值,你需要告诉它这个类型。
function Timer() {
+ const intervalRef = useRef<number | undefined>();
useEffect(() => {
const id = setInterval(() => {
// ...
});
intervalRef.current = id;
return () => {
clearInterval(intervalRef.current);
};
});
// ...
}
在DOM节点上使用useRef的一个经典用例是处理input元素的focus。
mport { useRef, useEffect } from 'react';
const AutoFocusInput = () => {
+ const inputRef = useRef(null);
useEffect(() => {
+ inputRef.current.focus();
}, []);
+ return <input ref={inputRef} type="text" value="前端柒八九" />;
};
export default AutoFocusInput;
TypeScript 有内置的DOM元素类型。这些类型的结构总是相同的:
如果
name是你正在使用的HTML标签的名称,相应的类型将是HTML${Name}Element。
这里有几个特例
<a>标签的类型为HTMLAnchorElement<h1>标签的类型为HTMLHeadingElement
对于<input>,该类型的名称将是HTMLInputElement。
mport { useRef, useEffect } from 'react';
const AutoFocusInput = () => {
+ const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
+ inputRef.current?.focus();
}, []);
return <input ref={inputRef} type="text" value="前端柒八九" />;
};
export default AutoFocusInput;
注意:在inputRef.current?.focus()上加了一个?。这是因为对于 TypeScript,inputRef.current可能是空的。在这种情况下,我们知道它不会是空的,因为它是在 useEffect 第一次运行之前由 React 填充的。
有时想把ref转发给子组件。要做到这一点,在 React 中我们必须用 forwardRef 来包装组件。
import { ChangeEvent } from 'react';
type Props = {
value: string,
handleChange: (event: ChangeEvent<HTMLInputElement>) => void,
};
const TextInput = ({ value, handleChange }: Props) => {
return <input type="text" value={value} onChange={handleChange} />;
};
例如,存在一个组件TextInput而我们想在父组件的调用处,通过ref来控制子组件input。
此时,就需要用forwardRef来处理。
import { forwardRef, ChangeEvent } from 'react';
type Props = {
value: string;
handleChange: (event: ChangeEvent<HTMLInputElement>) => void;
};
+const TextInput = forwardRef<HTMLInputElement, Props>(
+ ({ value, handleChange }, ref) => {
return (
+ <input ref={ref} type="text" value={value} onChange={handleChange} />
);
}
);
此语法只需要向 forwardRef 提供它应该期待的HTMLElement(在这种情况下是HTMLInputElement)。
有一点,需要指出:组件参数
ref和props的顺序与泛型的<HTMLInputElement, Props>不一样。
你不必给他们任何类型
唯一需要注意的是隐式返回。useEffect里面的回调应该是什么都不返回,或者是一个会清理任何副作用的Destructor函数(析构函数,这个词借用了C++中类的说法)
你不必给他们任何类型
为context提供类型是非常容易的。首先,为context的值创建一个类型,然后把它作为一个泛型提供给createContext函数。
import React, { createContext, useEffect, useState, ReactNode } from 'react';
+type User = {
+ name: string;
+ email: string;
+ freeTrial: boolean;
+};
+type AuthValue = {
+ user: User | null;
+ signOut: () => void;
+};
+const AuthContext = createContext<AuthValue | undefined>(undefined);
type Props = {
children: ReactNode;
};
const AuthContextProvider = ({ children }: Props) => {
const [user, setUser] = useState(null);
const signOut = () => {
setUser(null);
};
useEffect(() => {
// 副作用处理
}, []);
return (
+ <AuthContext.Provider value={{ user, signOut }}>
{children}
+ </AuthContext.Provider>
);
};
export default AuthContextProvider;
一旦你向createContext提供了泛型,剩余的事,都由ts为你代劳。
上述实现的一个问题是,就TypeScript而言,context的值可以是未定义的。也就是在我们使用context的值的时候,可能取不到。此时,ts可能会阻拦代码的编译。
如何解决context的值可能是未定义的情况呢。我们针对context的获取可以使用一个自定义的hook。
export const useAuthContext = () => {
const context = useContext(AuthContext);
if (context === undefined) {
throw new Error('useAuthContext必须在AuthContext上下文中使用');
}
return context;
};
通过类型保护,使得我们在使用context的时候,总是有值的。
类型化自定义
hook基本上和类型化普通函数一样
先处理onClick事件。React 提供了一个 MouseEvent 类型,可以直接使用!
import {
useState,
+ MouseEvent,
} from 'react';
export default function App() {
// 省略部分代码
+ const handleClick = (event: MouseEvent) => {
console.log('提交被触发');
};
return (
<div className="App">
<h1>前端柒八九</h1>
<button onClick={handleClick}>提交</button>
</div>
);
}
onClick事件实际上是由React维护的:它是一个合成事件。
合成事件是React对浏览器事件的一种包装,以便不同的浏览器,都有相同的API。
handleInputChange函数与 handleClick 非常相似,但有一个明显的区别。不同的是,ChangeEvent 是一个泛型,你必须提供什么样的DOM元素正在被使用。
import {
useState,
+ ChangeEvent
} from 'react';
export default function App() {
const [inputValue, setInputValue] = useState('');
+ const handleInputChange = (event: ChangeEvent<HTMLInputElement>) => {
setInputValue(event.target.value);
};
// 省略部分代码
return (
<div className="App">
<h1>前端柒八九</h1>
<input value={inputValue} onChange={handleInputChange} />
</div>
);
}
在上面的代码中需要注意的一点是,HTMLInputElement 特指HTML的输入标签。如果我们使用的是 textarea,我们将使用 HTMLTextAreaElement 来代替。
注意,MouseEvent 也是一个泛型,你可以在必要时对它进行限制。例如,让我们把上面的 MouseEvent 限制为专门从一个按钮发出的鼠标事件。
const handleClick = (event: MouseEvent<HTMLButtonElement>) => {
console.log('提交被触发');
};
还需要提示的是,React为我们提供了很多 Event 对象的类型声明。
| 事件类型 | 解释 |
|---|---|
ClipboardEvent<T = Element> | 剪切板事件对象 |
DragEvent<T =Element> | 拖拽事件对象 |
ChangeEvent<T = Element> | Change事件对象 |
KeyboardEvent<T = Element> | 键盘事件对象 |
MouseEvent<T = Element> | 鼠标事件对象 |
TouchEvent<T = Element> | 触摸事件对象 |
WheelEvent<T = Element> | 滚轮时间对象 |
AnimationEvent<T = Element> | 动画事件对象 |
TransitionEvent<T = Element> | 过渡事件对象 |
React 声明文件所提供的 EventHandler 类型别名,通过不同事件的 EventHandler 的类型别名来定义事件处理函数的类型,更方便定义其函数类型。

import {
useState,
+ ChangeEventHandler,
+ MouseEventHandler
} from 'react';
export default function App() {
const [inputValue, setInputValue] = useState('');
+ const handleInputChange: ChangeEventHandler<HTMLInputElement> = (event) =>{
setInputValue(event.target.value);
};
+ const handleClick: MouseEventHandler = (event) => {
console.log('提交被触发');
};
return (
// ...省略....
);
}
你也可以依靠类型推断,而不需要自己处理函数。但是,你需要将回调函数内联处理。
import { useState } from 'react';
export default function App() {
const [inputValue, setInputValue] = useState('');
return (
<div className="App">
<h1>前端柒八九</h1>
<input
value={inputValue}
+ onChange={(event) => setInputValue(event.target.value)}
/>
<button
+ onClick={(event) => console.log('提交被触发')}
>
提交
</button>
</div>
);
}
在 React 中提供两种形式,
而在类组件组件中需要继承 Component
class Welcome extends React.Component {
render() {
return <h1>Hello, 前端柒八九</h1>;
}
}
PureComponent:会对 props 和 state 进行浅比较,跳过不必要的更新,提高组件性能。
可以说 PureComponent 和 Component 基本完全一致,但 PureComponent 会浅比较,也就是较少render渲染的次数,所以PureComponent一般用于性能优化。
class Welcome extends React.PureComponent {
render() {
return <h1>Hello, 前端柒八九</h1>;
}
}
在生命周期中有一个 shouldComponentUpdate() 函数,shouldComponentUpdate()如果被定义,就会对新旧 props、state 进行 shallowEqual 比较,新旧一旦不一致,便会触发 update。
也可以这么理解:PureComponent 通过自带的 props 和 state 的浅比较实现了shouldComponentUpdate(),这点Component并不具备。
PureComponent可能会因深层的数据不一致而产生错误的否定判断,从而导致shouldComponentUpdate结果返回false,界面得不到更新,要谨慎使用。
memo:结合了 pureComponent 纯组件和 componentShouldUpdate()功能,会对传入的 props 进行一次对比,然后根据第二个函数返回值来进一步判断哪些props需要更新
要注意
memo是一个高阶组件,函数式组件和类组件都可以使用。
memo 接收两个参数:
function MyComponent(props) {
}
function areEqual(prevProps, nextProps) {
}
export default React.memo(MyComponent, areEqual);
(pre, next) => boolean,
pre:之前的数据next:现在的数据true 则不更新false 更新React.memo 与 PureComponent 的区别:
PureComponent 服务于类组件,React.memo既可以服务于类组件,也可以服务与函数式组件,useMemo 服务于函数式组件PureComponent 针对的是props和stateReact.memo只能针对props来决定是否渲染
React.memo的第二个参数的返回值与shouldComponentUpdate的返回值是相反的
React.memo:返回 true 组件不渲染 , 返回 false 组件重新渲染。shouldComponentUpdate: 返回 true 组件渲染 , 返回 false 组件不渲染
forwardRef:引用传递,是一种通过组件向子组件自动传递引用ref的技术。
在 React 中,React 不允许ref通过props传递,因为ref是组件中固定存在的,在组件调和的过程中,会被特殊处理,而forwardRef就是为了解决这件事而诞生的,让ref可以通过props传递。
// 子组件
const FancyButton = React.forwardRef((props, ref) => (
<button ref={ref} >
{props.children}
</button>
));
// 父组件范围内
const ref = React.createRef();
<FancyButton ref={ref}>谁点我!</FancyButton>;
在16.0后,官方推出了Fragment碎片概念,能够让一个组件返回多个元素,React.Fragment 等价于<></>
<></>的不同Fragment 这个组件可以赋值 key,也就是索引,<></>不能赋值lazy:允许你定义一个动态加载组件,这样有助于缩减 bundle 的体积,并延迟加载在初次渲染时未用到的组件,也就是懒加载组件(高阶组件)
lazy 接收一个函数,这个函数需要动态调用import(),如:
const SomeComponent = React.lazy(
() => import('./SomeComponent')
);
lazy必须接受一个函数,并且需要返回一个Promise, 并且需要 resolve 一个 default 一个React组件,lazy 必须要配合Suspense一起使用Suspense:让组件"等待"某个异步组件操作,直到该异步操作结束即可渲染。
const OtherComponent = React.lazy(
() => import('./OtherComponent')
);
function MyComponent() {
return (
// 在OtherComponent加载之前,显示Spinner
<React.Suspense fallback={<Spinner />}>
<OtherComponent />
</React.Suspense>
);
}
Profiler:这个组件用于性能检测,可以检测一次react组件渲染时的性能开销
此组件有两个参数:
id:标识Profiler的唯一性onRender:回调函数,组件在commit阶段被调用render(
<App>
<Profiler id="Navigation" onRender={callback}>
<Navigation {...props} />
</Profiler>
<Main {...props} />
</App>
);
StrictMode:严格模式,是一种用于突出显示应用程序中潜在问题的工具
与 Fragment 一样,StrictMode 也不会出现在UI层面,只是会检查和警告。
import React from 'react';
function ExampleApplication() {
return (
<div>
<Header />
<React.StrictMode>
<div>
<ComponentOne />
<ComponentTwo />
</div>
</React.StrictMode>
<Footer />
</div>
);
}
上述代码中只会对 ComponentOne 和ComponentTwo 进行检查。
从如下方面进行检测:
findDOMNode 的警告JSX 会被编译为React.createElement的形式,然后被babel编译
React.createElement(type, [props], [...children])
typeReactFragment[props]:对象,dom类中的属性,组件中的props[...children]:其他的参数,会依此排序cloneElement:克隆并返回一个新的React元素,
React.createElement(type, [props], [...children])
React.cloneElement()几乎等同于:
<element.type {...element.props} {...props}>
{children}
</element.type>
createContext 会创建一个Context对象,
Provider的value来传递值Consumer接受valueChildren: 提供处理this.props.children不透明数据结构的实用程序。
Children.map:遍历,并返回一个数组
const Child = ({children}) => {
const res = React.Children.map(children, (item) => item)
console.log(res)
return res
}
Children.forEach:与Children.map类似,不同的是Children.forEach并不会返回值,而是停留在遍历阶段
Children.count:返回Child的总个数,等于回调传递给map或forEach将被调用的次数
Children.only:验证Child是否只有一个元素,
Children.toArray:以平面数组的形式返回children不透明数据结构,每个子元素都分配有键。
createRef:创建一个ref对象,获取节点信息
isValidElement:用于验证是否是React元素,
true,false查看React的版本号
React 的 生命周期主要有两个比较大的版本,分别是
v16.0前v16.4两个版本的生命周期。

总共分为四大阶段:
在初始化阶段,会用到 constructor() 这个构造函数,如:
constructor(props) {
super(props);
}
super的作用
constructor() ),props注入给子组件,供子组件读取this.state的初始内容componentWillMount:在组件挂载到DOM前调用
this.setState不会引起组件的重新渲染,也可以把写在这边的内容提到constructor(),所以在项目中很少。render: 渲染
props和state发生改变(无论值是否有变化,两者的重传递和重赋值,都可以引起组件重新render),都会重新渲染render。return:是必须的,是一个React元素,不负责组件实际渲染工作,由React自身根据此元素去渲染出DOM。render 是纯函数,不能执行this.setState。componentDidMount:组件挂载到DOM后调用
componentWillReceiveProps(nextProps):调用于props引起的组件更新过程中
nextProps:父组件传给当前组件新的propsnextProps和this.props来查明重传props是否发生改变(原因:不能保证父组件重传的props有变化)props发生变化就会,引起调用shouldComponentUpdate(nextProps, nextState):用于性能优化
nextProps:当前组件的this.propsnextState:当前组件的this.statenextProps和nextState,来判断当前组件是否有必要继续执行更新过程。false:表示停止更新,用于减少组件的不必要渲染,优化性能true:继续执行更新componentWillReceiveProps()中执行了this.setState,更新了state,但在render前(如shouldComponentUpdate,componentWillUpdate),this.state依然指向更新前的state,不然nextState及当前组件的this.state的对比就一直是true了componentWillUpdate(nextProps, nextState):组件更新前调用
render方法前执行render:重新渲染
componentDidUpdate(prevProps, prevState):组件更新后被调用
prevProps:组件更新前的propsprevState:组件更新前的statecomponentWillUnmount:组件被卸载前调用
可以在这里执行一些清理工作,比如清楚组件中使用的定时器,清除componentDidMount中手动创建的DOM元素等,以避免引起内存泄漏

与 v16.0的生命周期相比
getXX)
getDerivedStateFromPropsgetSnapshotBeforeUpdatecomponmentWillXX)
componentWillMount、componentWillReceiveProps、componentWillUpdategetDerivedStateFromProps(prevProps, prevState):组件创建和更新时调用的方法
prevProps:组件更新前的propsprevState:组件更新前的state在
React v16.3中,在创建和更新时,只能是由父组件引发才会调用这个函数,在React v16.4改为无论是Mounting还是Updating,全部都会调用。
是一个静态函数,也就是这个函数不能通过this访问到class的属性。
如果
props传入的内容不需要影响到你的state,那么就需要返回一个null,这个返回值是必须的,所以尽量将其写到函数的末尾。
在组件创建时和更新时的render方法之前调用,它应该
null来不更新任何内容getSnapshotBeforeUpdate(prevProps,prevState):Updating时的函数,在render之后调用
prevProps:组件更新前的propsprevState:组件更新前的state可以读取,但无法使用DOM的时候,在组件可以在可能更改之前从DOM捕获一些信息(例如滚动位置)
返回的任何指都将作为参数传递给componentDidUpdate()
在17.0的版本,官方彻底废除
componentWillMount、componentWillReceiveProps、componentWillUpdatereact-hooks是React 16.8的产物,给函数式组件赋上了生命周期。
useState:定义变量,可以理解为他是类组件中的this.state
使用:
const [state, setState] = useState(initialState);
state:目的是提供给 UI,作为渲染视图的数据源setState:改变 state 的函数,可以理解为this.setStateinitialState:初始默认值
useState有点类似于PureComponent,会进行一个比较浅的比较,如果是对象的时候直接传入并不会更新。
useEffect:副作用,你可以理解为是类组件的生命周期,也是我们最常用的钩子
副作用(
Side Effect):是指function做了和本身运算返回值无关的事,如请求数据、修改全局变量,打印、数据获取、设置订阅以及手动更改React组件中的DOM都属于副作用操作
useEffect不设立第二个参数时,无论什么情况,都会执行useEffect的第二个值useContext:上下文,类似于Context:其本意就是设置全局共享数据,使所有组件可跨层级实现数据共享
useContent的参数一般是由createContext的创建,通过 CountContext.Provider 包裹的组件,才能通过 useContext 获取对应的值
useReducer:它类似于redux功能的api
const [state, dispatch] = useReducer(reducer, initialArg, init);
state:更新后的state值dispatch:可以理解为和useState的setState一样的效果reducer:可以理解为redux的reducerinitialArg:初始值init:惰性初始化useMemo:与memo的理念上差不多,都是判断是否满足当前的限定条件来决定是否执行callback函数,而useMemo的第二个参数是一个数组,通过这个数组来判定是否执行回调函数
当一个父组件中调用了一个子组件的时候,父组件的
state发生变化,会导致父组件更新,而子组件虽然没有发生改变,但也会进行更新。
只要父组件的状态更新,无论有没有对子组件进行操作,子组件都会进行更新,useMemo就是为了防止这点而出现的。
useCallback与useMemo极其类似,唯一不同的是
useMemo返回的是函数运行的结果,useCallback返回的是函数
React.memo,否则不但不会提升性能,还有可能降低性能useRef: 可以获取当前元素的所有属性,并且返回一个可变的ref对象,并且这个对象只有current属性,可设置initialValue
React元素的属性值useImperativeHandle:可以让你在使用 ref 时自定义暴露给父组件的实例值
useImperativeHandle(ref, createHandle, [deps])
ref:useRef所创建的refcreateHandle:处理的函数,返回值作为暴露给父组件的 ref 对象。deps:依赖项,依赖项更改形成新的 ref 对象。
useImperativeHandle和forwardRef配合使用
function FancyInput(props, ref) {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
}
}));
return <input ref={inputRef} ... />;
}
FancyInput = forwardRef(FancyInput);
在父组件中,可以渲染<FancyInput ref={inputRef} />并可以通过父组件的inputRef对子组件中的input进行处理。inputRef.current.focus()
useLayoutEffect: 与useEffect基本一致,不同的地方时,useLayoutEffect是同步
要注意的是useLayoutEffect在 DOM 更新之后,浏览器绘制之前,这样做的好处是可以更加方便的修改 DOM,获取 DOM 信息,这样浏览器只会绘制一次,所以useLayoutEffect在useEffect之前执行
如果是 useEffect 的话 ,useEffect 执行在浏览器绘制视图之后,如果在此时改变DOM,有可能会导致浏览器再次回流和重绘。
除此之外useLayoutEffect的 callback 中代码执行会阻塞浏览器绘制
useDebugValue:可用于在 React 开发者工具中显示自定义 hook 的标签
useSyncExternalStore:是一个推荐用于读取和订阅外部数据源的 hook,其方式与选择性的 hydration 和时间切片等并发渲染功能兼容
const state = useSyncExternalStore(
subscribe,
getSnapshot[, getServerSnapshot]
)
subscribe: 订阅函数,用于注册一个回调函数,当存储值发生更改时被调用。此外, useSyncExternalStore 会通过带有记忆性的 getSnapshot 来判别数据是否发生变化,如果发生变化,那么会强制更新数据。getSnapshot: 返回当前存储值的函数。必须返回缓存的值。如果 getSnapshot 连续多次调用,则必须返回相同的确切值,除非中间有存储值更新。getServerSnapshot:返回服务端(hydration模式下)渲染期间使用的存储值的函数useTransition:
过渡任务
在一些场景中,如:输入框、tab切换、按钮等,这些任务需要视图上立刻做出响应,这些任务可以称之为立即更新的任务
但有的时候,更新任务并不是那么紧急,或者来说要去请求数据等,导致新的状态不能立马更新,需要用一个loading...的等待状态,这类任务就是过度任务
const [isPending, startTransition] = useTransition();
isPending:过渡状态的标志,为true时是等待状态startTransition:可以将里面的任务变成过渡任务useDeferredValue:接受一个值,并返回该值的新副本,该副本将推迟到更紧急地更新之后。
如果当前渲染是一个紧急更新的结果,比如用户输入,React 将返回之前的值,然后在紧急渲染完成后渲染新的值。
也就是说useDeferredValue可以让状态滞后派生。
const deferredValue = useDeferredValue(value);
value:可变的值,如useState创建的值deferredValue: 延时状态useTransition和useDeferredValue做个对比
useDeferredValue 和 useTransition 一样,都是过渡更新任务useTransition 给的是一个状态,而useDeferredValue给的是一个值useInsertionEffect:与 useLayoutEffect 一样,但它在所有 DOM 突变之前同步触发
在执行顺序上 useInsertionEffect > useLayoutEffect > useEffect
seInsertionEffect应仅限于css-in-js库作者使用。优先考虑使用useEffect或useLayoutEffect来替代。
useId : 是一个用于生成横跨服务端和客户端的稳定的唯一 ID 的同时避免hydration不匹配的 hook。
createPortal:在Portal中提供了一种将子节点渲染到 DOM 节点中的方式,该节点存在于 DOM 组件的层次结构之外。
也就是说 createPortal 可以把当前组件或element元素的子节点,渲染到组件之外的其他地方。
来看看createPortal(child, container)的入参:
flushSync:可以将回调函数中的更新任务,放到一个较高级的优先级中,适用于强制刷新,同时确保了DOM会被立即更新
onClick={() => {
this.setState({ number: 1 })
this.setState({ number: 2 })
this.setState({ number: 3 })
}}
因为this.setState会进行批量更新,所以打印出的是3
onClick={() => {
this.setState({ number: 1 })
ReactDOM.flushSync(()=>{
this.setState({ number: 2 })
})
this.setState({ number: 3 })
}}
发现flushSync会优先执行,并且强制刷新,所以会改变number值为2,然后1和3在被批量刷新,更新为3
render:这个是我们在React-Dom中最常用的Api,用于渲染一个React元素
ReactDOM.render(
< App / >,
document.getElementById('app')
)
在React v18中,render函数已经被createRoot所替代。
createRoot会控制你传入的容器节点的内容。当调用 render 时,里面的任何现有 DOM 元素都会被替换。后面的调用使用 React 的 DOM diffing 算法进行有效更新。
并且 createRoot 不修改容器节点(只修改容器的子节点)。可以在不覆盖现有子节点的情况下将组件插入现有 DOM 节点。
const rootElement = document.getElementById('root');
const root = createRoot(rootElement);
root.render(
<StrictMode>
<Main />
</StrictMode>
);
unmountComponentAtNode:从 DOM 中卸载组件,会将其事件处理器(event handlers)和 state 一并清除。如果指定容器上没有对应已挂载的组件,这个函数什么也不会做。
unmountComponentAtNode 同样在React 18中被替代了,替换成了createRoot中的unmount()方法
const root = createRoot(container);
root.render(element);
root.unmount()
findDOMNode:用于访问组件DOM元素节点(应急方案),官方推荐使用ref
unstable_batchedUpdates :可用于手动批量更新state,可以指定多个setState合并为一个更新请求
分享是一种态度。
全文完,既然看到这里了,如果觉得不错,随手点个赞和“在看”吧。

本文由mdnice多平台发布
|
|
| JavaScript知识库 最新文章 |
| ES6的相关知识点 |
| react 函数式组件 & react其他一些总结 |
| Vue基础超详细 |
| 前端JS也可以连点成线(Vue中运用 AntVG6) |
| Vue事件处理的基本使用 |
| Vue后台项目的记录 (一) |
| 前后端分离vue跨域,devServer配置proxy代理 |
| TypeScript |
| 初识vuex |
| vue项目安装包指令收集 |
|
|
| 上一篇文章 下一篇文章 查看所有文章 |
|
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
| 360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年11日历 | -2025/11/29 12:02:55- |
|
| 网站联系: qq:121756557 email:121756557@qq.com IT数码 |