学习React 18需要了解的Hook
1. useDeferredValue
useDeferredValue
是一个非常有用的hook,它可以用来推迟某个值的更新,以提高性能。它在处理用户输入或其他频繁变化的值时特别有用。通过将某个值包装在useDeferredValue
中,React会推迟该值的更新,直到浏览器处于空闲状态。
import { useDeferredValue, useState } from 'react';
const SlowUI = () => (
<>
{Array(50000)
.fill(1)
.map((_, index) => (
<span key={index}>{100000} </span>
))}
</>
);
function App() {
const [value, setValue] = useState(0);
const deferredValue = useDeferredValue(value);
const handleClick = () => {
setValue(value + 1);
};
return (
<>
<button onClick={handleClick}>{value}</button>
<div>DeferredValue: {deferredValue}</div>
<div>
<SlowUI />
</div>
</>
);
}
export default App;
2. useTransition
useTransition()是一个用于过渡效果的hook。它返回过渡的状态和一个用于启动过渡的函数。
const [isPending, startTransition] = useTransition();
React状态更新可以分为两类:
紧急更新 - 反映直接的交互,例如键入、点击、按下、拖动等。
过渡更新 - 用于将UI从一个视图过渡到另一个视图。
在过渡中,更新会让位给更紧急的更新。下面是一个示例,将useTransition
放置在src/App.js
中:
import React, { useState, useTransition } from 'react';
function App() {
const [isPending, startTransition] = useTransition();
const [text, setText] = useState('');
const handleChange = (event) => {
startTransition(() => {
setText(event.target.value);
});
};
return (
<div>
<input type="text" value={text} onChange={handleChange} />
{isPending ? <p>Loading...</p> : <p>Text: {text}</p>}
</div>
);
}
export default App;
在上述代码中,我们使用useTransition()
hook来获取过渡的状态和startTransition()
函数。我们还使用useState()
hook来创建一个文本输入框的状态和更新函数。在handleChange
函数中,我们使用startTransition()
函数将文本更新操作包裹起来,以确保它在过渡期间能够让位给更紧急的更新。当isPending
为true时,显示"Loading...",否则显示文本的内容。
3. useMutableSource
useMutableSource
是一个高级的hook,它可以用来创建可变的数据源,以便在自定义的React Hooks中共享数据。它可以用来解决一些复杂的状态共享问题。
import { useMutableSource } from 'react';
function useCustomHook() {
const mutableSource = useMutableSource(myMutableSource, getSnapshot, subscribe);
// 使用mutableSource进行状态管理和更新
return mutableSource;
}
4. useId
在Web应用程序中,有些情况下需要使用唯一的ID,例如:
<label for="ID">
,其中for属性必须与相关元素的id属性相等,以将它们绑定在一起。
aria-labelledby,其中aria-labelledby属性可以接受多个ID。
useId()
是一个生成唯一ID的hook:
这个ID在服务器和客户端之间是稳定的,避免了服务器端渲染时的混合问题。
这个ID在整个应用程序中是唯一的。对于多根应用程序,createRoot/hydrateRoot
有一个可选的identifierPrefix
属性,可以用于添加前缀以防止冲突。
这个ID可以附加前缀和/或后缀,以生成在组件中使用的多个唯一ID。这似乎是一个微不足道的功能。但是,useId
是从useOpaqueIdentifier
演变而来的,后者生成的是不可操作的不透明ID。
使用useId
可以帮助我们生成唯一的ID,确保在应用程序中的各个组件中使用不重复的ID。这对于构建可靠的用户界面和确保无障碍性非常重要。
请注意,useId
是一个自定义hook,并不是React的内置hook,你可以根据自己的需求实现它或使用现有的库,如下使用:
import { useId } from 'react';
const Comp1 = () => {
const id = useId();
return <div>Comp1 id({id})</div>;
};
const Comp2 = () => {
const id = useId();
return (
<>
<div>Comp2 id({id})</div>
<label htmlFor={`${id}-1`}>Label 1</label>
<div>
<input id={`${id}-1`} type="text" />
</div>
<label htmlFor={`${id}-2`}>Label 2</label>
<div>
<input id={`${id}-2`} type="text" />
</div>
</>
);
};
const Comp3 = () => {
const id = useId();
return (
<>
<div>Comp3 id({id})</div>
<div aria-labelledby={`${id}-a ${id}-b ${id}-c`}>I am Comp3</div>
</>
);
};
function App() {
return (
<>
<Comp1 />
<Comp2 />
<Comp3 />
</>
);
}
export default App;
5. useSyncExternalStore
useSyncExternalStore是一个推荐用于从外部数据源(存储)中读取和订阅数据的hook。
下面是该hook的函数签名:
const state = useSyncExternalStore(subscribe, getSnapshot[, getServerSnapshot]);
该方法接受三个参数:
subscribe
:一个函数,用于注册一个回调函数,该回调函数在存储更改时被调用。
getSnapshot
:一个函数,返回存储的当前值。
getServerSnapshot
:一个函数,返回在服务器渲染期间使用的快照。这是一个可选参数。
该方法返回存储的值,即state
。
如下代码:
import { useSyncExternalStore } from 'react';
function App() {
const width = useSyncExternalStore(
(listener) => {
window.addEventListener('resize', listener);
return () => {
window.removeEventListener('resize', listener);
};
},
() => window.innerWidth
// () => -1,
);
return <p>Size: {width}</p>;
}
export default App;
在上述代码中,调用了useSyncExternalStore
:
subscribe
:它为窗口调整大小事件监听器注册了一个回调函数。
getSnapshot
:它返回当前浏览器窗口的宽度。
getServerSnapshot
:这是用于服务器渲染的,但在此处不需要,可以简单地返回-1。
6. useInsertionEffect
useInsertionEffect
是在React 18中引入的一个新的hook。它具有与useEffect
相同的签名,但在所有DOM变化之前同步触发。也就是说,它在useLayoutEffect之
前触发。它用于在读取布局之前向DOM插入样式。
useInsertionEffect
主要用于CSS-in-JS库,例如styled-components。由于此hook的范围有限,它无法访问refs
并且无法调度更新。
以下示例比较了useEffect
、useLayoutEffect
和useInsertionEffect
:
import { useEffect, useLayoutEffect, useInsertionEffect } from 'react';
function App() {
useEffect(() => {
console.log('useEffect');
return () => {
console.log('Cleanup useEffect');
};
}, []);
useLayoutEffect(() => {
console.log('useLayoutEffect');
return () => {
console.log('Cleanup useLayoutEffect');
};
}, []);
useInsertionEffect(() => {
console.log('useInsertionEffect');
return () => {
console.log('Cleanup useInsertionEffect');
};
}, []);
return <div>Hello World</div>;
}
export default App;
在上述代码中,我们使用了三个不同的effect hook:useEffect
、useLayoutEffect
和useInsertionEffect
。每个effect hook都输出一条消息,并在组件卸载时进行清理。
当我们运行这个示例时,我们可以观察到它们的触发顺序和清理顺序。useEffect
在组件渲染后触发,useLayoutEffect
在DOM变化之后触发,而useInsertionEffect
在DOM变化之前触发。
请注意,useInsertionEffect
是React 18中新增的一个hook,并且在特定的使用场景下才会使用。通常情况下,使用useEffect
和useLayoutEffect
就足够满足大多数需求。
7. useSyncExternalState
useSyncExternalState
是一个用于同步外部状态的hook,在React 18中引入了对外部状态同步的原生支持。它可以用来将React组件的状态与外部的状态进行同步。
import { useSyncExternalState } from 'react';
function MyComponent() {
const [count, setCount] = useState(0);
// 将外部状态与组件状态同步
useSyncExternalState(count, setCount);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
<button onClick={() => setCount(count - 1)}>Decrement</button>
</div>
);
}
在上述代码中,我们使用useSyncExternalState
将组件的count
状态与外部的状态进行同步。这样,当外部的状态发生变化时,组件的状态也会相应地更新。
8. useErrorHandler
useErrorHandler
是一个用于处理错误的hook,在React 18中引入了对错误处理的原生支持。它可以用来捕获和处理组件中发生的错误。
import { useErrorHandler } from 'react';
function MyComponent() {
const handleError = useErrorHandler();
const fetchData = async () => {
try {
const response = await fetch('api/data');
const data = await response.json();
// 处理获取的数据
} catch (error) {
handleError(error);
}
};
return (
<div>
<button onClick={fetchData}>Fetch Data</button>
</div>
);
}
在上述代码中,我们使用useErrorHandler
来获取一个错误处理函数,并在fetchData
函数中捕获和处理错误。这样,当发生错误时,我们可以通过错误处理函数来进行相应的处理。
评论(0)