react高级_揭秘高级React钩

react高级

This is not an introductory article. Let’s be clear about that. I am assuming you have experience using React. In this article, I will try to clarify with examples of how and when to use some of the advanced react hooks. Towards the end, I will also show how to write your own hooks.

这不是介绍性文章。 让我们澄清一下。 我假设您有使用React的经验。 在本文中,我将尝试通过示例说明如何以及何时使用一些高级react挂钩。 最后,我还将展示如何编写自己的钩子。

为什么要钩? (Why hooks?)

You can read about the motivation behind hooks either here or for the sake of time and brevity, below.

您可以在此处或为了节省时间和简洁而在下面阅读有关挂钩背后的动机。

Most of the time, complex class components can be difficult to break down into smaller units because of their stateful logic. As the development progress, they become wordy, complicated, confusing and after a certain point, much difficult to read and manage. Hooks allow us to achieve “separation of concerns” by letting us extract component logic into smaller and simpler independent reusable functions. So each time a hook is called, it initializes a local state and encapsulates it within the currently executing component. So, by using hooks, we have:

在大多数情况下,复杂的类组件由于其状态逻辑而难以分解为较小的单元。 随着开发的进展,它们变得冗长,复杂,令人困惑,并且在特定点之后很难阅读和管理。 挂钩允许我们通过将组件逻辑提取到更小和更简单的独立可重用函数中来实现“关注点分离”。 因此,每次调用钩子时,它都会初始化一个本地状态并将其封装在当前执行的组件中。 因此,通过使用钩子,我们可以:

  • No complex patterns.

    没有复杂的模式。
  • No code duplication.

    没有代码重复。
  • Increased code readability.

    提高了代码的可读性。

Also as a bonus, no more dealing with the this keyword.

作为奖励,不再使用this关键字。

While we’re at it, here’s a quick note on when to use hooks. Basically:

在此过程中, 这是有关何时使用钩子的简要说明。 基本上:

  • Call hooks only at the top level — never inside loops, conditions or nested function. Why? Read this article by Rudi Yardley on unpacking how hooks works. The same hook can be used multiple times inside of a function but only and only at the top level.

    仅在顶层调用钩子-绝不在循环,条件或嵌套函数内。 为什么? 阅读文章由亿利鲁迪开箱挂钩是如何工作的。 相同的挂钩可以在函数内部多次使用,但只能且仅在顶层使用。

  • Call hooks only from React function component — calling hooks inside a hook is valid.

    仅从React函数组件调用钩子-在钩子内部调用钩子是有效的。

这么多钩子! (So many hooks!)

React provides the following built-in hooks:

React提供了以下内置的钩子:

  • useState

    useState
  • useEffect

    useEffect
  • useContext

    useContext
  • useReducer

    useReducer
  • useCallback

    useCallback
  • useMemo

    useMemo
  • useRef

    useRef
  • useImperativeHandle

    useImperativeHandle
  • useLayoutEffect

    useLayoutEffect
  • useDebugValue

    useDebugValue

The first three are the basic hooks and the next 7 are the advanced hooks. In this article, I will try to explain the advanced hooks with the exception of useReducer. I suspect many of you are or have been using useReducer in conjunction with useContext for the purpose of state management. Besides, there are tons of resources on the same, so check those out.

前三个是基本挂钩,接下来的七个是高级挂钩。 在本文中,我将尝试解释高级钩子,但useReducer 。 我怀疑你们中的许多人正在或已经将useReduceruseContext一起用于状态管理。 此外,同一资源有很多,请检查一下。

useRef (useRef)

useRef returns a mutable ref object whose .current property is initialized to the passed argument (initialValue). The returned object will persist for the full lifetime of the component.

useRef返回一个可变的ref对象, .current对象的.current属性已初始化为传递的参数( initialValue )。 返回的对象将在组件的整个生命周期内保持不变。

Typically in react, a child component re-renders whenever the parent component passes new props to it. However, at times we may want to reference the HTML element directly to modify it imperatively once it has mounted on the DOM. To access the actual HTML element we need to create a react reference and pass is to the element.

通常,在react中,子组件在父组件将新道具传递给它时会重新渲染。 但是,有时我们可能希望直接引用HTML元素,以便在将其安装在DOM上后进行必要的修改。 要访问实际HTML元素,我们需要创建一个react引用,并将传递给该元素。

Here’s an example of how we’d use useRef to reference an input element for auto focusing. We’re telling react to set focus on the input element on the DOM when the component mounts. The .current property holds the mutable <input/> object.

这是一个示例,说明如何使用useRef引用输入元素以进行自动聚焦。 我们说的是在组件安装时将注意力集中在DOM上的input元素上。 .current属性包含可变的<input/>对象。

import React, { useRef, useEffect } from 'react';
import './App.css';const App = ( props ) => {
const inputRef = useRef(null);
const buttonRef = useRef(null);
useEffect(
() => {
inputRef.current.focus()
}, []
); return(
<div className="container">
<input type="text"
placeholder="Enter some text here"
name="name"
ref={inputRef} />
<button ref={buttonRef}> Done! </button>
</div>
);
}export default App;

Try out the code above. You’ll see that as soon as the DOM loads, the input is focused. Some of the most common use cases for using a ref are managing focus, determining element sizes, triggering animations, integrating with DOM manipulating plugins and so on. For your information you can also use a react ref for storing mutable values such as a number or a string or boolean (like you would in useState) if you want to prevent react from re-rendering the DOM. React refs are not reactive. See below to understand what I mean.

试试上面的代码。 您会看到,一旦DOM加载,输入就会被聚焦。 使用ref的一些最常见的用例是管理焦点,确定元素大小,触发动画,与DOM操纵插件集成等等。 为了您的信息,如果要防止useState重新呈现DOM,还可以使用react ref存储可变值,例如数字或字符串或布尔值(就像useState )。 React ref是无React的。 请参阅下文以了解我的意思。

import React, { useRef } from 'react';
import './App.css';const App = ( props ) => {
const intRef = useRef(0); const increase = () => {
intRef.current += 1;
console.log(`Current value is ${intRef.current}`);
} const decrease = () => {
intRef.current -= 1;
console.log(`Current value is ${intRef.current}`);
} return (
<div className='container'>
<div>
Current value is {intRef.current}
</div>
<div>
<button onClick={increase}> + </button>
<button onClick={decrease}> - </button>
</div>
</div>
)
}export default App;
Image for post

Even though the integer value is actually changing, there is no change on the DOM. The value rendered on the DOM is still 0. If we had used useState instead of useRef the value on the DOM would also change. However, please don’t do this unless you really know what you’re doing! Use useState instead. It’s much cleaner, nicer and sane.

即使整数值实际上在变化,DOM上也没有变化。 在DOM上呈现的值仍为0。如果我们使用useState而不是useRef ,则DOM上的值也会更改。 但是,除非您真的知道自己在做什么,否则请不要这样做! 请改用useState 。 它更干净,更好,更理智。

This is all good! We know how to reference a DOM element from within our react code. BUT! What if you have built your own custom function component and want to allow other developers to take a ref to it? Here’s where you can use forwarding refs. From react doc:

一切都很好! 我们知道如何在我们的React代码中引用DOM元素。 但! 如果您构建了自己的自定义功能组件并希望允许其他开发人员对此进行引用该怎么办? 您可以在这里使用转发引用。 从react doc

If you want to allow people to take a ref to your function component, you can use forwardRef (possibly in conjunction with useImperativeHandle), or you can convert the component to a class.

如果要允许人们对功能组件进行ref ,则可以使用forwardRef (可能与useImperativeHandle结合使用),也可以将组件转换为类。

This will have to wait until we reach the useImperativeHandle hook.

这将不得不等到到达useImperativeHandle钩子。

useCallback (useCallback)

Pass an inline callback and an array of dependencies. useCallback will return a memoized version of the callback that only changes if one of the dependencies has changed. This is useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders.

传递内联回调和依赖项数组。 useCallback将返回回调的记忆版本,仅在其中一个依赖项已更改时才更改。 当将回调传递给依赖于引用相等性的优化子组件以防止不必要的渲染时,这很有用。

Quick recap of reference equality. Here’s a very small example.

快速回顾参考相等性。 这是一个非常小的例子。

console.log([1, 2] === [1, 2])
// falseconst c = [1, 2]
console.log(c === c)
// true

In the first case even though both objects seem identical, they are actually not because these objects are in different locations in memory. In the second case, c refers to the same object i.e. itself. Referential equality checks to see if two given objects refer to the same memory location. What this means in JavaScript is that two objects that look identical are not the same unless they both point at the same object.

在第一种情况下,即使两个对象看起来相同,但实际上并非如此,因为这两个对象位于内存中的不同位置。 在第二种情况下, c指的是同一对象,即本身。 引用相等性检查以查看两个给定的对象是否引用相同的内存位置。 这在JavaScript中的意思是,看起来相同的两个对象除非它们都指向相同的对象,否则它们是不相同的。

When react components are about to re-render, it compares each object in the components to the newly created version of itself. Since all objects in the new version are created anew, they refer to different memory locations thus allowing react to re-render. Therefore, all functions are recreated as well. The purpose of useCallback is to prevent unnecessary recreation of functions and instead return the same function. Only when one of the dependencies changes a new function will be created. This article by Nikolay Grozev explains nicely how and when to use useCallback which I have reproduced below:

当React组件将要重新渲染时,它将组件中的每个对象与自身的新创建版本进行比较。 由于新版本中的所有对象都是重新创建的,因此它们引用了不同的存储位置,因此可以对重新渲染做出React。 因此,所有功能也会重新创建。 useCallback的目的是防止不必要的函数重新生成,而是返回相同的函数。 仅当其中一个依赖项更改时,才会创建新功能。 Nikolay Grozev的这篇文章很好地解释了如何以及何时使用useCallback ,我在下面对此进行了转载:

import React, { useState, useCallback } from 'react';
import './App.css';const totalFunctions = new Set();const App = ( props ) => {const [delta, setDelta] = useState(0);
const [total, setTotal] = useState(0);const increaseTotal = () => setTotal(total => total+delta);
const decreaseTotal = () => setTotal(total => total-delta);
const increaseDelta = () => setDelta(delta => delta+1);
const decreaseDelta = () => setDelta(delta => delta-1);totalFunctions.add(increaseTotal);
totalFunctions.add(decreaseTotal);
totalFunctions.add(increaseDelta);
totalFunctions.add(decreaseDelta);return (
<div className='container'>
<div>
Current total is {total}
</div>
<div>
<button onClick={increaseTotal}> + </button>
<button onClick={decreaseTotal}> - </button>
</div>
<div>
Current delta is {delta}
</div>
<div>
<button onClick={increaseDelta}> + </button>
<button onClick={decreaseDelta}> - </button>
</div>
<div>
Total functions {totalFunctions.size}
</div>
</div>
)
}export default App;

Check out what happens when you run the code above. Notice the total functions count. Initially it is 4. And each time react re-renders it creates 4 new functions.

看看运行上面的代码会发生什么。 注意功能总数。 最初是4。每次重新渲染时,它都会创建4个新功能。

Image for post

Now think about this — do we really need to create new increaseTotal and decreaseTotal functions each time when delta itself does not change? We can optimize this by using useCallback. After modifying the code below with useCallback:

现在考虑一下-当增量本身不改变时,是否真的需要创建新的increaseTotaldecreaseTotal函数? 我们可以使用useCallback对此进行优化。 用useCallback修改以下代码后:

import React, { useState, useCallback } from 'react';
import './App.css';const totalFunctions = new Set();const App = ( props ) => {const [delta, setDelta] = useState(0);
const [total, setTotal] = useState(0);const increaseTotal = useCallback(() => setTotal(total => total+delta), [delta]);
const decreaseTotal = useCallback(() => setTotal(total => total-delta), [delta]);
const increaseDelta = useCallback(() => setDelta(delta => delta+1), []);
const decreaseDelta = useCallback(() => setDelta(delta => delta-1), []);totalFunctions.add(increaseTotal);
totalFunctions.add(decreaseTotal);
totalFunctions.add(increaseDelta);
totalFunctions.add(decreaseDelta);return (
<div className='container'>
<div>
Current total is {total}
</div>
<div>
<button onClick={increaseTotal}> + </button>
<button onClick={decreaseTotal}> - </button>
</div>
<div>
Current delta is {delta}
</div>
<div>
<button onClick={increaseDelta}> + </button>
<button onClick={decreaseDelta}> - </button>
</div>
<div>
Total functions {totalFunctions.size}
</div>
</div>
)
}export default App;

Now check out what happens when you run the code above. Again notice the total functions count.

现在查看运行以上代码时发生的情况。 再次注意总功能数。

Image for post

Initially the count of total functions is 4. And each time react re-renders it creates 2 new functions and these are the new versions of increaseTotal and decreaseTotal both of which has delta as the dependency. So only when delta changes, react creates new functions. We have also set increaseDelta and decreaseDelta functions to be created only once at the start since these functions are not dependent on anything.

最初的总函数的计数是4而每一次React重新呈现它创建2层新的功能和这些是新版本的increaseTotaldecreaseTotal这两者具有delta作为依赖性。 因此,仅当delta发生变化时,react才会创建新函数。 我们还设置了increaseDelta函数和decreaseDelta increaseDelta函数在开始时只能创建一次,因为这些函数不依赖任何内容。

useCallback has reduced the number of functions recreations significantly, and thus memory is optimized. However, for small use cases like above, it may be an overkill.

useCallback显着减少了重新创建功能的数量,因此优化了内存。 但是,对于像上面这样的小型用例,这可能是一个过大的杀伤力。

useMemo (useMemo)

Pass a “create” function and an array of dependencies. useMemo will only recompute the memoized value when one of the dependencies has changed. This optimization helps to avoid expensive calculations on every render.

传递“创建”函数和一系列依赖项。 useMemo当一个依赖已经改变,只会重新计算memoized值。 此优化有助于避免对每个渲染进行昂贵的计算。

useMemo is very similar to useCallback. In fact, react doc itself states

useMemouseCallback非常相似。 实际上,react doc本身指出

useCallback(fn, deps) is equivalent to useMemo(() => fn, deps)

useCallback(fn, deps)等同于useMemo(() => fn, deps)

useMemo calls the function when one of the dependencies change and returns an object. To clarify, useCallback returns a cached uncalled function unless one of its dependencies changes and useMemo returns a cached object computed by the function unless one of its dependencies changes. The returned object may very well be a function as in above. So we may say that useMemo is the generalized form of useCallback. This hook is useful when we want to prevent any child component from re-rendering unnecessarily when the parent component re-renders. Imagine you have a parent component that contains a child component which has a large list. Every time the parent is re-rendered, the child will also be re-rendered. If the list is huge it will slow down the app’s performance. See an example below:

当依赖项之一更改并返回一个对象时, useMemo调用该函数。 为了明确useCallbackuseCallback返回一个缓存的未调用函数,除非其依赖关系之一发生更改,而useMemo返回由该函数计算的缓存对象,除非其依赖关系之一发生变化。 返回的对象很可能是上面的函数。 所以,我们可以说, useMemo是广义形式useCallback 。 当我们要防止父组件重新渲染时不必要地重新渲染任何子组件时,此钩子很有用。 想象一下,您有一个父组件,其中包含一个子组件,该子组件的列表很大。 每当父母被重新渲染时,孩子也将被重新渲染。 如果列表很大,将会降低应用程序的性能。 请参阅以下示例:

import React from 'react';
import './App.css';
const App = () => {
const [todos, setTodos] = React.useState(["Bake cookies", "Buy milk", "Buy eggs", "Buy avocado"]);
const [text, setText] = React.useState('');
const handleAddUser = () =>{
setTodos([text, ...todos]);
};
const handleRemove = (index) => {
todos.splice(index, 1);
setTodos([...todos]);
};const renderList = () => {
console.log("Render List");
return todos.map((item, index) => (
<li key={index} style={{padding: 5}}>
<button type="button" onClick={() => handleRemove(index)}> - </button>
{item}
</li>
))
}console.log('Render App');
return (
<div className="container">
<div>
<input type="text" value={text} onChange={evt => setText(evt.target.value)} />
<button type="button" onClick={handleAddUser}> + </button>
</div>
<ul>
{renderList()}
</ul>
</div>
);
};
export default App;
Image for post

See the console log. Without memoizing, the list is rendered every time when the user input changes. This is an extremely small list here. But imagine if the list contains hundreds of thousands of items! Therefore, we need a way to prevent unnecessary re-renders by memoizing the list and only re-render it when an item is added or removed from the list. And for this react provides the useMemo hook. Below is the same code modified with useMemo.

请参阅控制台日志。 无需记忆,每次用户输入更改时都会显示该列表。 这是一个非常小的列表。 但是,请想象列表中是否包含成千上万的物品! 因此,我们需要一种方法,通过记住列表并仅在添加或删除列表中的项目时才重新呈现它,从而防止不必要的重新渲染。 为此,提供了useMemo钩子。 以下是使用useMemo修改的相同代码。

import React, { useCallback, useMemo } from 'react';
import './App.css';
const App = () => {
const [todos, setTodos] = React.useState(["Bake cookies", "Buy milk", "Buy eggs", "Buy avocado"]);
const [text, setText] = React.useState('');
const handleAddUser = () =>{
setTodos([text, ...todos]);
};const handleRemove = useCallback((index) => {
todos.splice(index, 1);
setTodos([...todos]);
}, [todos]);const renderMemoizedList = useMemo(() => {
console.log("Render Memoized List");
return todos.map((item, index) => (
<li key={index} style={{padding: 5}}>
<button type="button" onClick={() => handleRemove(index)}> - </button>
{item}
</li>
))
}, [todos, handleRemove]);console.log('Render App');
return (
<div className="container">
<div>
<input type="text" value={text} onChange={evt => setText(evt.target.value)} />
<button type="button" onClick={handleAddUser}> + </button>
</div>
<ul>
{renderMemoizedList}
</ul>
</div>
);
};
export default App;

Now check out the result below. Every time we type in the input, the app re-renders and logs to the console. However, the list is re-rendered only when we add or remove an item from the list. Notice that handleRemove is wrapped inside useCallback. Can you figure why?

现在检查下面的结果。 每次我们输入输入时,应用都会重新渲染并登录到控制台。 但是,仅当我们从列表中添加或删除项目时,列表才会重新呈现。 请注意, handleRemove包装在useCallback 。 你能弄清楚为什么吗?

Image for post

To recapitulate, useCallback is used to memoize functions, useMemo is used to memoize objects.

概括起来, useCallback用于useCallback功能,useMemo用于记忆对象。

useImperativeHandle (useImperativeHandle)

useImperativeHandle customizes the instance value that is exposed to parent components when using ref. As always, imperative code using refs should be avoided in most cases. useImperativeHandle should be used with forwardRef.

useImperativeHandle自定义使用ref时公开给父组件的实例值。 与往常一样,在大多数情况下应避免使用ref的命令性代码。 useImperativeHandle应该与forwardRef.一起使用forwardRef.

This does exactly what it says — customizes the instance value that is exposed to parent component when using ref. In 99% of use cases, we wouldn’t need this because there are always better ways of getting things done. We will look into an example of useImperativeHandle and then also see how it could be avoided. Imagine we’re designing a Drawer that shows on button click. We will use useImperativeHandle with React.forwardRef to expose some properties of the drawer to the parent component. A little basic css animation is used to achieve the transformation.

这完全符合它的意思-自定义使用ref时公开给父组件的实例值。 在99%的用例中,我们不需要这样做,因为总是有更好的方法来完成任务。 我们将研究一个useImperativeHandle的示例,然后还要了解如何避免这种情况。 想象一下,我们正在设计一个在单击按钮时显示的抽屉。 我们将useImperativeHandleReact.forwardRef一起使用,以将抽屉的某些属性公开给父组件。 使用一些基本CSS动画来实现转换。

import React, { useState, useRef, useImperativeHandle, forwardRef } from 'react';
import './App.css';import books from './books';const Drawer = forwardRef((props, ref) => {
const [styleClass, setStyleClass] = useState("drawer");
useImperativeHandle(ref, () => ({
show: () => setStyleClass("drawer show"),
hide: () => setStyleClass("drawer hide"),
})); return (
<div className={styleClass} onClick={props.onClick}>
{props.children}
</div>
);
});
const App = () => {
const ref = useRef(null);
const hideDrawer = () => ref.current.hide();
return (
<div className="container">
<Drawer ref={ref} onClick={hideDrawer}>
{
books.map((book, index) => <div key={index} className="content"> {book} </div> )
}
</Drawer>
<button onClick={() => ref.current.show()}> Show drawer </button>
</div>
);
};
export default App;
Image for post

Neat animation, isn’t it? With this example, we can also see how animation could be triggered using reference. But, the important thing to notice here of course is how useImperativeHandle and forwardRef is used here. Using this hook we have exposed show and hide from the component Drawer to its parent App. But as stated before, there are nicer ways of getting this done and that is by using useEffect. Below is the code using useEffect and guess what? We don’t need to use useRef, forwardRef or useImperativeHandle. Sweet, right?

整洁的动画,不是吗? 通过此示例,我们还可以看到如何使用引用触发动画。 但是,这里要注意的重要事项当然是在这里如何使用useImperativeHandleforwardRef 。 使用此挂钩,我们将showhide从组件Drawer暴露给其父App 。 但是,如前所述,有更好的方法可以完成此任务,即使用useEffect 。 下面是使用useEffect的代码,您猜怎么着? 我们不需要使用useRefforwardRefuseImperativeHandle 。 亲爱的,对吗?

import React, { useState, useEffect } from 'react';
import './App.css';import books from './books';const Drawer = (props) => {
const [styleClass, setStyleClass] = useState("drawer");
const [isInitialLoad, setIsInitialLoad] = useState(true);useEffect(() => {
if(isInitialLoad){
setIsInitialLoad(false);
} else if(props.isOpen){
setStyleClass("drawer show");
} else {
setStyleClass("drawer hide");
}
}, [isInitialLoad, props.isOpen]);return (
<div className={styleClass} onClick={props.onClick}>
{props.children}
</div>
);
};
const App = () => { const [isOpen, setIsOpen] = useState(false);
const hideDrawer = () => setIsOpen(false);
return (
<div className="container">
<Drawer isOpen={isOpen} onClick={hideDrawer}>
{
books.map((book, index) => <div key={index} className="content"> {book} </div> )
}
</Drawer>
<button onClick={() => setIsOpen(true)}> Show drawer </button>
</div>
);
};
export default App;

This code produces the exact same result as before. We have to use a state isInitialLoad to prevent the animation from running for the first time since useEffect always runs when react renders for the first time. Remember, in 99% of our use cases, we can achieve using useEffect what we intend to achieve using useImperativeHandle. However, in some very rare cases we may need to use it.

此代码产生与以前完全相同的结果。 我们必须使用isInitialLoad状态,以防止动画第一次运行,因为useEffect始终在第一次useEffect渲染时运行。 请记住,在我们99%的用例中,我们可以使用useEffect实现我们打算使用useImperativeHandle实现的useImperativeHandle 。 但是,在极少数情况下,我们可能需要使用它。

useLayoutEffect (useLayoutEffect)

The signature is identical to useEffect, but it fires synchronously after all DOM mutations. Use this to read layout from the DOM and synchronously re-render. Updates scheduled inside useLayoutEffect will be flushed synchronously, before the browser has a chance to paint.

签名与useEffect相同,但是在所有DOM突变后,它都会同步触发。 使用它从DOM读取布局并同步重新渲染。 在浏览器有机会绘制之前,在useLayoutEffect内部计划的更新将被同步刷新。

The last hooks we will look at is useLayoutEffect. The important line to notice above is:

我们将要看的最后一个挂钩是useLayoutEffect 。 上面要注意的重要内容是:

The signature is identical to useEffect, but it fires synchronously after all DOM mutations.

签名与useEffect相同,但是在所有DOM突变后,它都会同步触发。

useEffect(() => {
// do something
}, [dependencies])useLayoutEffect(() => {
// do something
}, [dependencies])

However, the difference between the two is that useEffect runs asynchronously whereas useLayoutEffect runs synchronously right after the DOM mutations are computed. In other words, useEffect is deferred until after the browser is painted whereas useLayoutEffect does not wait for browser window to finish repainting before executing. This is so that the user do not perceive any visual inconsistency in the layout due to the DOM mutations. However before any new render occurs useEffect is guaranteed to execute. Check out the code and the rendering below.

但是,两者之间的区别在于useEffect异步运行,而useLayoutEffect在计算DOM突变后useLayoutEffect同步运行。 换句话说, useEffect推迟到绘制浏览器之后,而useLayoutEffect在执行之前不等待浏览器窗口完成重新绘制。 这样,由于DOM突变,用户不会感觉到布局中的任何视觉不一致。 但是,在任何新的渲染发生useEffect都会保证useEffect执行。 在下面查看代码和渲染。

import React, { useEffect } from 'react';
import './App.css';const App = () => {
const style = {
width: "40px",
height: "40px",
backgroundColor: "rgb(57, 245, 235)",
borderRadius: "50%"
} useEffect(() => {
const circle = document.getElementById("circle");
circle.style.transform = "translate(157%, 127%)";
circle.style.left = "259%"
circle.style.top = "377%"
}); return (
<div id="container">
<div id="circle" style={ style } />
</div>
);
}export default App;
Image for post

See the circle flicker? This occurs because since useEffect executes after App has been rendered, the new attributes to the circle’s style is added after it’s been painted on the DOM. Therefore, when the circle translates to its new location, we perceive a slight flicker. Change useEffect to useLayoutEffect and we won’t notice this flicker anymore because by the time the browser window is repainted the new position of the circle would already have been calculated.

看到圆圈闪烁了吗? 发生这种情况的原因是,由于useEffect在渲染App之后执行,因此在DOM上绘制圆形之后,会添加新的圆形样式属性。 因此,当圆平移到新位置时,我们会感觉到轻微的闪烁。 更改useEffectuseLayoutEffect因为当时的浏览器窗口重新绘制将已经被计算圆的新位置,我们将不会再看到这种抖动。

useDebugValue (useDebugValue)

useDebugValue can be used to display a label for custom hooks in React DevTools.

useDebugValue可用于在React DevTools中显示自定义钩子的标签。

That’s it. Seriously. It just displays a label in React DevTools. Now if you look at the react hooks document, you will notice a sub section Defer formatting debug values. It explains that useDebugValue takes a second parameter — a formatting function for when formatting the value you want to display is an expensive function. Okay… Uh? What? Why would I have an expensive formatting operation? That’s just wrong!

而已。 说真的 它只是在React DevTools中显示一个标签。 现在,如果您查看react hooks文档,您会注意到Defer formatting debug values子节。 它说明了useDebugValue具有第二个参数-用于格式化要显示的值的格式化函数是一项昂贵的函数。 好吧... 什么? 为什么我要进行昂贵的格式化操作? 那就错了!

Anyhoo! IMO, just use console log. I can safely say console log is the most used method, albeit may not be the favorite one, for debugging by more than 90% developers.

hoo! IMO,只需使用控制台日志。 我可以肯定地说,控制台日志是最常用的方法,尽管可能不是最受欢迎的一种方法,但90%以上的开发人员都在进行调试。

WYODH (WYODH)

Yes. If you want to make your code modular, easily readable and reusable then write your own damn hooks. Hooks are incredible. With that said, you should be able to write your own hooks and here’s an example.

是。 如果您想使代码模块化,易于阅读和可重用,请编写自己的该死的钩子。 钩子太不可思议了。 话虽如此,您应该可以编写自己的钩子,这是一个示例。

import React, { useState } from 'react';const useNetworkStatus = () => {
const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
const [type, setType] = useState(connection.effectiveType) connection.addEventListener('change', () => setType(connection.effectiveType)); return [navigator.onLine, navigator.onLine ? type : "undefined"];
}const App = () => {
const [isOnline, networkType] = useNetworkStatus();
return (
<div style={{margin: "20px 0px", width: '100%', textAlign: 'center'}}>
<div>Network is {isOnline ? "Online" : "Offline"}</div
<div>Network type is {networkType}</div>
</div>
);
}

In the example above, we have created a hook to check if the connection is online or not and what the type of the connection is. For this I used the Network Information API which you can find here. When you run the code, open the developers tool on chrome, go to the Network tab and change the network throttling profile (available options are Online (default), Fast 3G, Slow 3G and Offline). You’ll see the text on the page changes accordingly.

在上面的示例中,我们创建了一个钩子来检查连接是否在线以及连接的类型。 为此,我使用了Network Information API,您可以在此处找到它。 运行代码时,在chrome上打开开发人员工具,转到“网络”标签,然后更改网络限制配置文件(可用选项为“在线”(默认),“快速3G”,“慢速3G”和“离线”。 您会看到页面上的文本发生相应变化。

NOTE: You can test the code above on any modern browsers except IE, Safari and Safari for iOS. But I assume most of you are using Chrome, so this should work fantastic.

注意:您可以在IE,Safari和iOS版Safari以外的任何现代浏览器上测试以上代码。 但是我想你们中的大多数人都在使用Chrome,所以这应该效果不错。

So there you have it. Advanced react hooks demystified. In the end, I would throw in a caveat though— using these hooks adds an overhead to the code. First understand where optimize needs to be done and then use these hooks.

所以你有它。 先进的React钩揭开神秘面纱。 最后,我会提出警告,使用这些钩子会增加代码的开销。 首先了解需要在何处进行优化,然后使用这些挂钩。

Enjoy using hooks and writing your own damn hooks and break a leg!

喜欢使用钩子并编写自己的该死的钩子并摔断一条腿!

翻译自: https://medium.com/swlh/demystifying-advanced-react-hooks-6e6672d4cab6

react高级

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值