当前位置: 首页 > news >正文

ReactHooks 官网文档翻译

useCallback(fn, dependencies)

useCallback是一个React Hook,它允许您在重新渲染之间缓存函数定义。

const cachedFn = useCallback(fn, dependencies)

1、参数:

fn:要缓存的函数值。它可以接受任何参数并返回任何值。React将在初始渲染期间返回(而不是调用!)您的函数。在下一次渲染中,如果自上次渲染以来依赖项没有更改,React将再次为您提供相同的函数。否则,它将为您提供在当前渲染过程中传递的函数,并将其存储起来,以备以后重用。React不会调用您的函数。函数将返回给您,以便您可以决定何时以及是否调用它。

dependencies:fn代码内部引用的所有反应值的列表。反应值包括props、state以及直接在组件体内声明的所有变量和函数。如果您的linter配置为React,它将验证每个React值是否正确指定为依赖项。依赖项列表必须具有恒定数量的项,并且像[dep1,dep2,dep3]一样以内联方式编写。React将使用Object.is的比较算法将每个依赖项与其以前的值进行比较。

2、返回值:

在初始渲染时,useCallback返回您传递的fn函数。
在随后的渲染过程中,它将从上次渲染中返回一个已经存储的fn函数(如果依赖项没有更改),或者返回您在此渲染过程中传递的fn函数。

3、告诫:

useCallback是一个Hook,所以您只能在组件的顶层或您自己的Hook中调用它。不能在循环或条件内部调用它。如果需要,请提取一个新组件并将状态移动到其中。

4、用法:

在以下渲染中,React将把依赖项与您在上一次渲染中传递的依赖项进行比较。如果任何依赖项都没有更改(与Object.is相比),useCallback将返回与以前相同的函数。否则,useCallback将返回在此渲染中传递的函数。

import { useCallback } from 'react';export default function ProductPage({ productId, referrer, theme }) {const handleSubmit = useCallback((orderDetails) => {post('/product/' + productId + '/buy', {referrer,orderDetails,});}, [productId, referrer]);

默认情况下,当组件重新渲染时,React会递归地重新渲染其所有子级。这就是为什么当ProductPage使用不同的主题重新渲染时,ShippingForm组件也会重新渲染。这对于不需要太多计算即可重新渲染的组件来说很好。但是,如果验证了重新渲染的速度较慢,则可以通过将其包装在备忘录中,告诉ShippingForm在其道具与上次渲染相同时跳过重新渲染

function ProductPage({ productId, referrer, theme }) {// ...return (<div className={theme}><ShippingForm onSubmit={handleSubmit} /></div>);

使用了memo包裹子组件只有prop变化时才会重新渲染,否则就跳过重新渲染

import { memo } from 'react';const ShippingForm = memo(function ShippingForm({ onSubmit }) {// ...
});

在JavaScript中,函数(){}或()=>{}总是创建不同的函数,类似于{}对象文字总是创建新对象的方式。通常情况下,这不会是一个问题,但这意味着ShippingForm道具将永远不会相同,您的备忘录优化也不会起作用。在这个例子中父组件的prop变化时函数会重新创建,即使子组件使用memeo依然不起作用,因为handleSubmit每次都不同

function ProductPage({ productId, referrer, theme }) {// Tell React to cache your function between re-renders...const handleSubmit = useCallback((orderDetails) => {post('/product/' + productId + '/buy', {referrer,orderDetails,});}, [productId, referrer]); // ...so as long as these dependencies don't change...return (<div className={theme}>{/* ...ShippingForm will receive the same props and can skip re-rendering */}<ShippingForm onSubmit={handleSubmit} /></div>);
}

现在我们就可以将他等着在useCallback,只有依赖项变化时才会返回新的函数定义。一般情况下我们不必将函数使放在useCallback中,除非处于某种特定原因

function ProductPage({ productId, referrer, theme }) {// Tell React to cache your function between re-renders...const handleSubmit = useCallback((orderDetails) => {post('/product/' + productId + '/buy', {referrer,orderDetails,});}, [productId, referrer]); // ...so as long as these dependencies don't change...return (<div className={theme}>{/* ...ShippingForm will receive the same props and can skip re-rendering */}<ShippingForm onSubmit={handleSubmit} /></div>);
}

5、useCallack和useMemo 的区别

useMemo缓存调用函数的结果。useCallback缓存函数本身。

 const requirements = useMemo(() => { // Calls your function and caches its resultreturn computeRequirements(product);}, [product]);const handleSubmit = useCallback((orderDetails) => { // Caches your function itselfpost('/product/' + productId + '/buy', {referrer,orderDetails,});}, [productId, referrer]);

可以想象成这样:

// Simplified implementation (inside React)
function useCallback(fn, dependencies) {return useMemo(() => fn, dependencies);
}

6、何时使用useCallack

如果你的应用程序像这个网站,并且大多数交互都很粗糙(比如替换一个页面或整个部分),那么通常不需要记忆。另一方面,如果你的应用程序更像一个绘图编辑器,并且大多数交互都是细粒度的(比如移动的形状),那么你可能会发现记忆非常有用。
使用useCallback缓存函数只有在少数情况下才有价值:
1、您将函数作为餐厨传递给了memo包裹的子组件
2、您传递的函数稍后将用作某个Hook的依赖项。例如,包装在useCallback中的另一个函数依赖于它,或者您依赖于useEffect中的此函数。
在其他情况下,用useCallback包装函数没有任何好处。这样做也没有重大危害,所以一些团队选择不考虑个别案例,并尽可能多地记忆。缺点是代码的可读性变差。此外,并非所有的记忆都是有效的:一个“总是新的”值就足以破坏整个组件的记忆。

useContext(SomeContext)

useContext是一个React Hook,它允许您从组件中读取和订阅上下文。

import { useContext } from 'react';function MyComponent() {const theme = useContext(ThemeContext);// ...

1、参数

SomeContext:您之前使用createContext创建的上下文。

2、返回值

useContext返回调用组件的上下文值,返回的值始终是最新的

3、告诫

相应的<Context.Provider>需要位于执行useContext()调用的组件之上。

4、用法

深层数据传递、通过context传递更新数据
react 会找桑钱上下文最近的provider,不论层级多深都可以使用useContext(ThemeContext),访问到

function MyPage() {return (<ThemeContext.Provider value="dark"><Form /></ThemeContext.Provider>);
}function Form() {// ... renders buttons inside ...
}

useEffect(setup, dependencies?)

useEffect是一个React Hook,用于将组件与外部系统同步。

import { useEffect } from 'react';
import { createConnection } from './chat.js';function ChatRoom({ roomId }) {const [serverUrl, setServerUrl] = useState('https://localhost:1234');useEffect(() => {const connection = createConnection(serverUrl, roomId);connection.connect();return () => {connection.disconnect();};}, [serverUrl, roomId]);// ...
}

1、参数

setup:具有Effect逻辑的函数。您的设置函数也可以选择返回一个清理函数。
可选依赖项:设置代码内部引用的所有反应值的列表。反应值包括props、state以及直接在组件体内声明的所有变量和函数。

2、用法

连接到外部系统:某些组件在页面上显示时,需要保持与网络、某些浏览器API或第三方库的连接。这些系统不受React控制,所以它们被称为外部系统。
例如:
一个由setInterval()和clearInterval.()管理的计时器。
使用window.addEventListener()和window.removeEventLister()的事件订阅。
带有API的第三方动画库,如animation.start()和animation.reset()

3、解释

感觉官网的解释有些晦涩,一般我们是这么理解的
可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合。
情况一:不传第二个参数(模拟 class 组件的 DidMount 和 DidUpdate )

useEffect(() => {
/** 执行逻辑 */
}) 

情况二:传递一个空数组(模拟 class 组件的 DidMount )

useEffect(() => {
/** 执行逻辑 */
},[]) 

情况三:传递数组有依赖项(模拟 class 组件的 DidUpdate )

useEffect(() => {
/** 执行逻辑 */
},[age,name])  

情况四:第一个参数可以返回一个回调函数(模拟 WillUnMount 组件销毁的时候 停止计时器 )

const [time, setTime] = useState(0)
useEffect(() => {const InterVal = setInterval(() => {setTime(time + 1)},1000)return () => {clearInterval(InterVal )}
},[])

useMemo(calculateValue, dependencies)

useMemo是一个React Hook,可以在重新渲染之间缓存计算结果。

import { useMemo } from 'react';function TodoList({ todos, tab }) {const visibleTodos = useMemo(() => filterTodos(todos, tab),[todos, tab]);// ...
}

1、参数

calculateValue:计算要缓存的值的函数。
dependencies:calculateValue代码内部引用的所有反应值的列表。

2、返回值

在初始呈现时,useMemo返回不带参数的调用calculateValue的结果。
在接下来的渲染过程中,它将返回上次渲染中已经存储的值(如果依赖项没有更改),或者再次调用calculateValue,并返回calculateValue返回的结果。

3、用法

跳过昂贵的计算

4、怎样算昂贵的计算

一般来说,除非您正在创建或循环数千个对象,否则这可能并不昂贵。如果你想获得更多的信心,你可以添加一个控制台日志来测量在一段代码中花费的时间:

console.time('filter array');
const visibleTodos = filterTodos(todos, tab);
console.timeEnd('filter array');

如果记录的总时间加起来相当大(比如1毫秒或更长),那么将计算结果记忆起来可能是有意义的。作为实验,您可以将计算结果封装在useMemo中,以验证该交互的总记录时间是否减少。

5、何时使用useMemo

1、计算较慢,并且依赖值很少变化
2、将该函数结果作为参数传给memo包裹的子组件
3、该结果作为其他hook的依赖项
在其他情况下,将计算包装在useMemo中没有任何好处。这样做也没有重大危害,所以一些团队选择不考虑个别案例,并尽可能多地记忆。这种方法的缺点是代码的可读性变差

useState(initialState)

useState是一个React Hook,它允许您将状态变量添加到组件中。

import { useState } from 'react';function MyComponent() {const [age, setAge] = useState(28);const [name, setName] = useState('Taylor');const [todos, setTodos] = useState(() => createTodos());// ...

1、参数

initialState:您希望状态初始为的值。它可以是任何类型的值,但函数有一种特殊的行为。此参数在初始渲染后将被忽略。
如果将函数作为initialState传递,它将被视为初始值设定项函数。

2、返回值

current state :当前状态,在第一次渲染期间,它将与您通过的initialState相匹配。
set函数:用于将状态更新为不同的值并触发重新渲染。您可以直接传递下一个状态,也可以传递根据上一个状态计算的函数

setName('Taylor');setAge(a => a + 1);

3、用法

向组件添加状态,React将存储下一个状态,使用新值再次渲染组件,并更新UI。

import { useState } from 'react';function MyComponent() {const [age, setAge] = useState(42);const [name, setName] = useState('Taylor');// ...

4、定义对象

您可以将对象和数组置于状态。在React中,状态被认为是只读的,所以您应该替换它,而不是突变现有的对象。

// 🚩 Don't mutate an object in state like this:
form.firstName = 'Taylor';
// ✅ Replace state with a new object
setForm({...form,firstName: 'Taylor'
});

5、避免传入函数调用结果为初始值

 const [todos, setTodos] = useState(createInitialTodos());

尽管createInitialTodos()的结果仅用于初始渲染,但您仍在每次渲染时调用此函数。如果创建大型数组或执行昂贵的计算,这可能是浪费。

  const [todos, setTodos] = useState(createInitialTodos);

要解决此问题,可以将其作为初始值设定项函数传递给useState您传递的是函数本身createInitialTodos,而不是调用它的结果createInitialTodos()。如果您将函数传递给useState,React只会在初始化期间调用它。

useRef(initialValue)

useRef是一个React Hook,它允许您引用渲染不需要的值。

mport { useRef } from 'react';function MyComponent() {const intervalRef = useRef(0);const inputRef = useRef(null);// ...

1、参数

initialValue:您希望ref对象的当前属性初始为的值。它可以是任何类型的值。此参数在初始渲染后将被忽略。

2、返回值

current:最初,它被设置为您传递的initialValue。您可以稍后将其设置为其他内容。

3、用法

①使用ref定义一个无需引起页面重新渲染的状态
改变一个ref的值不会触发重新渲染。这意味着引用非常适合存储不影响组件视觉输出的信息。

function handleStopClick() {const intervalId = intervalRef.current;clearInterval(intervalId);
}

②使用ref操作DOM

使用ref来操作DOM是特别常见的。React对此有内置支持。
首先,声明一个初始值为null的ref对象:

import { useRef } from 'react';function MyComponent() {const inputRef = useRef(null);// ...return <input ref={inputRef} />;

React创建DOM节点并将其放在屏幕上后,React将把ref对象的当前属性设置为该DOM节点。现在,您可以访问<input>的DOM节点,并调用focus()等方法:
③ 避免重新创建引用内容
React将初始ref值保存一次,并在下次渲染时忽略它。

function Video() {const playerRef = useRef(new VideoPlayer());

尽管新VideoPlayer()的结果仅用于初始渲染,但您仍在每次渲染时调用此函数。如果创建昂贵的对象,这可能是浪费。
要解决此问题,您可以按如下方式初始化ref:

function Video() {const playerRef = useRef(null);if (playerRef.current === null) {playerRef.current = new VideoPlayer();}// ...

4、将ref传给自定义组件

默认情况下,组件不会将其DOM节点公开给父组件,这么写会报错

const inputRef = useRef(null);
return <MyInput ref={inputRef} />;

如果您希望MyInput的父组件能够访问<input>DOM节点,则必须选择使用forwardRef:

import { forwardRef } from 'react';const MyInput = forwardRef(({ value, onChange }, ref) => {return (<inputvalue={value}onChange={onChange}ref={ref}/>);
});export default MyInput;

useImperativeHandle(ref, createHandle, dependencies?)

1、解释

React的useImperativeHandle是自定义钩子,可以让父组件获取并执行子组件内某些自定义函数(方法),或者获取子组件的状态。

2、本质

useImperativeHandle本质上其实是子组件将自己内部的函数(方法)通过useImperativeHandle添加到父组件中useRef定义的对象中。如果想使用useImperativeHandle,那么还要结合useRef、React.forwardRef一起使用。

3、示例

结合这个例子:
使用forwardRef后父组件可以访问子组件的Dom

import { forwardRef } from 'react';const MyInput = forwardRef(({ value, onChange }, ref) => {return (<inputvalue={value}onChange={onChange}ref={ref}/>);
});export default MyInput;

假设您不想公开整个<input>DOM节点,但希望公开它的两个方法:focus和scrollIntoView。要做到这一点,请将真实的浏览器DOM保存在一个单独的引用中。然后使用useImperativeHandle只使用您希望父组件调用的方法来公开句柄:

import { forwardRef, useRef, useImperativeHandle } from 'react';const MyInput = forwardRef(function MyInput(props, ref) {const inputRef = useRef(null);useImperativeHandle(ref, () => {return {focus() {inputRef.current.focus();},scrollIntoView() {inputRef.current.scrollIntoView();},};}, []);return <input {...props} ref={inputRef} />;
});

现在,如果父组件获得对MyInput的引用,它将能够调用其上的焦点和scrollIntoView方法。但是,它将无法完全访问底层的<input>DOM节点。

相关文章:

  • React进阶 - 14(说一说”虚拟DOM“中的”Diff算法“)
  • 马尔可夫预测(Python)
  • 【日常聊聊】自然语言处理的发展
  • 央视推荐的护眼灯是哪款?护眼灯品牌推荐
  • LeetCode 每日一题 Day 51 - 53
  • spring(一):基于XML获取Bean对象以及各种依赖注入方式
  • 【iOS ARKit】BlendShapes
  • Golang 通过开源库 go-redis 操作 NoSQL 缓存服务器
  • 256:vue+openlayers利用高德逆地理编码,点击地图,弹出某点坐标和地址信息
  • xcode 设置 ios苹果图标,为Flutter应用程序配置iOS图标
  • 一个处理Range List的面试题解法
  • Python爬虫 - 网易云音乐下载
  • 什么是IDE?新手用哪个IDE比较好?
  • iPhone备份数据怎么恢复?恢复苹果数据只需看这3招
  • React16源码: React中的completeWork中对不同类型节点处理的源码实现
  • 【MySQL经典案例分析】 Waiting for table metadata lock
  • Android路由框架AnnoRouter:使用Java接口来定义路由跳转
  • CSS实用技巧干货
  • GitUp, 你不可错过的秀外慧中的git工具
  • Gradle 5.0 正式版发布
  • JS专题之继承
  • Median of Two Sorted Arrays
  • 分享一份非常强势的Android面试题
  • 和 || 运算
  • 聊聊hikari连接池的leakDetectionThreshold
  • 聊聊spring cloud的LoadBalancerAutoConfiguration
  • 使用 5W1H 写出高可读的 Git Commit Message
  • 小程序上传图片到七牛云(支持多张上传,预览,删除)
  • 用简单代码看卷积组块发展
  • 运行时添加log4j2的appender
  • 《码出高效》学习笔记与书中错误记录
  • LevelDB 入门 —— 全面了解 LevelDB 的功能特性
  • linux 淘宝开源监控工具tsar
  • ​TypeScript都不会用,也敢说会前端?
  • ​业务双活的数据切换思路设计(下)
  • #14vue3生成表单并跳转到外部地址的方式
  • #大学#套接字
  • (14)Hive调优——合并小文件
  • (一)kafka实战——kafka源码编译启动
  • (中等) HDU 4370 0 or 1,建模+Dijkstra。
  • (转)memcache、redis缓存
  • ***利用Ms05002溢出找“肉鸡
  • .NET Standard、.NET Framework 、.NET Core三者的关系与区别?
  • .net 简单实现MD5
  • .net 中viewstate的原理和使用
  • .net实现客户区延伸至至非客户区
  • .pop ----remove 删除
  • :O)修改linux硬件时间
  • ??如何把JavaScript脚本中的参数传到java代码段中
  • @cacheable 是否缓存成功_让我们来学习学习SpringCache分布式缓存,为什么用?
  • @staticmethod和@classmethod的作用与区别
  • [ vulhub漏洞复现篇 ] Celery <4.0 Redis未授权访问+Pickle反序列化利用
  • [AutoSar]工程中的cpuload陷阱(三)测试
  • [boost]使用boost::function和boost::bind产生的down机一例
  • [C#]猫叫人醒老鼠跑 C#的委托及事件